From fb795f6bfdbb0638c21ae33e2f02e025ca38d780 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 16 Dec 2020 13:00:19 +0900 Subject: [PATCH 01/60] Add initial hook-up to spectator backend --- .../Visual/Gameplay/TestSceneSpectator.cs | 2 +- .../TestSceneSpectatorDrivenLeaderboard.cs | 74 +++++++++++++++++++ 2 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorDrivenLeaderboard.cs diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs index 3e5b561a6f..1fdff99da6 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs @@ -232,7 +232,7 @@ namespace osu.Game.Tests.Visual.Gameplay public class TestSpectatorStreamingClient : SpectatorStreamingClient { - public readonly User StreamingUser = new User { Id = 1234, Username = "Test user" }; + public readonly User StreamingUser = new User { Id = 55, Username = "Test user" }; public new BindableList PlayingUsers => (BindableList)base.PlayingUsers; diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorDrivenLeaderboard.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorDrivenLeaderboard.cs new file mode 100644 index 0000000000..7211755ba6 --- /dev/null +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorDrivenLeaderboard.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 System; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Game.Database; +using osu.Game.Online.API; +using osu.Game.Online.Spectator; +using osu.Game.Rulesets.Osu.Scoring; +using osu.Game.Screens.Play.HUD; + +namespace osu.Game.Tests.Visual.Gameplay +{ + public class TestSceneSpectatorDrivenLeaderboard : OsuTestScene + { + [Cached(typeof(SpectatorStreamingClient))] + private TestSceneSpectator.TestSpectatorStreamingClient testSpectatorStreamingClient = new TestSceneSpectator.TestSpectatorStreamingClient(); + + // used just to show beatmap card for the time being. + protected override bool UseOnlineAPI => true; + + [SetUp] + public void SetUp() => Schedule(() => + { + OsuScoreProcessor scoreProcessor; + + testSpectatorStreamingClient.StartPlay(55); + + Children = new Drawable[] + { + scoreProcessor = new OsuScoreProcessor(), + new MultiplayerGameplayLeaderboard(scoreProcessor) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + } + }; + }); + } + + public class MultiplayerGameplayLeaderboard : GameplayLeaderboard + { + private readonly OsuScoreProcessor scoreProcessor; + + public MultiplayerGameplayLeaderboard(OsuScoreProcessor scoreProcessor) + { + this.scoreProcessor = scoreProcessor; + + AddPlayer(new BindableDouble(), new GuestUser()); + } + + [Resolved] + private SpectatorStreamingClient streamingClient { get; set; } + + [Resolved] + private UserLookupCache userLookupCache { get; set; } + + [BackgroundDependencyLoader] + private void load() + { + Console.WriteLine("got here"); + + foreach (var user in streamingClient.PlayingUsers) + { + streamingClient.WatchUser(user); + var resolvedUser = userLookupCache.GetUserAsync(user).Result; + AddPlayer(new BindableDouble(), resolvedUser); + } + } + } +} From 2954218897e90eb54a692063c0e4af5cf882d28c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 16 Dec 2020 15:25:20 +0900 Subject: [PATCH 02/60] Add method to ScoreProcessor to calculate score and accuracy from statistics --- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 39 +++++++++++++++++++-- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index 499673619f..b4f29d7a6e 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -68,7 +68,12 @@ namespace osu.Game.Rulesets.Scoring private readonly double comboPortion; private int maxAchievableCombo; - private double maxBaseScore; + + /// + /// The maximum achievable base score. + /// + public double MaxBaseScore { get; private set; } + private double rollingMaxBaseScore; private double baseScore; @@ -196,7 +201,7 @@ namespace osu.Game.Rulesets.Scoring private double getScore(ScoringMode mode) { return GetScore(mode, maxAchievableCombo, - maxBaseScore > 0 ? baseScore / maxBaseScore : 0, + MaxBaseScore > 0 ? baseScore / MaxBaseScore : 0, maxAchievableCombo > 0 ? (double)HighestCombo.Value / maxAchievableCombo : 1, scoreResultCounts); } @@ -227,6 +232,34 @@ namespace osu.Game.Rulesets.Scoring } } + /// + /// Given a minimal set of inputs, return the computed score and accuracy for the tracked beatmap / mods combination. + /// + /// The to compute the total score in. + /// The maximum combo achievable in the beatmap. + /// Statistics to be used for calculating accuracy, bonus score, etc. + /// The computed score and accuracy for provided inputs. + public (double score, double accuracy) GetScoreAndAccuracy(ScoringMode mode, int maxCombo, Dictionary statistics) + { + // calculate base score from statistics pairs + int computedBaseScore = 0; + + foreach (var pair in statistics) + { + if (!pair.Key.AffectsAccuracy()) + continue; + + computedBaseScore += Judgement.ToNumericResult(pair.Key) * pair.Value; + } + + double accuracy = MaxBaseScore > 0 ? computedBaseScore / MaxBaseScore : 0; + double comboRatio = maxAchievableCombo > 0 ? (double)HighestCombo.Value / maxAchievableCombo : 1; + + double score = GetScore(mode, maxAchievableCombo, accuracy, comboRatio, scoreResultCounts); + + return (score, accuracy); + } + private double getBonusScore(Dictionary statistics) => statistics.GetOrDefault(HitResult.SmallBonus) * Judgement.SMALL_BONUS_SCORE + statistics.GetOrDefault(HitResult.LargeBonus) * Judgement.LARGE_BONUS_SCORE; @@ -266,7 +299,7 @@ namespace osu.Game.Rulesets.Scoring if (storeResults) { maxAchievableCombo = HighestCombo.Value; - maxBaseScore = baseScore; + MaxBaseScore = baseScore; } baseScore = 0; From d009a0be51defe7890002ca501d666c40e29fbd9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 16 Dec 2020 15:25:27 +0900 Subject: [PATCH 03/60] Move class to final location --- .../HUD/MultiplayerGameplayLeaderboard.cs | 98 +++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs diff --git a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs new file mode 100644 index 0000000000..1d9fdd9ded --- /dev/null +++ b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs @@ -0,0 +1,98 @@ +// 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.Bindables; +using osu.Game.Configuration; +using osu.Game.Database; +using osu.Game.Online.API; +using osu.Game.Online.Spectator; +using osu.Game.Rulesets.Scoring; + +namespace osu.Game.Screens.Play.HUD +{ + public class MultiplayerGameplayLeaderboard : GameplayLeaderboard + { + private readonly ScoreProcessor scoreProcessor; + + /// + /// Construct a new leaderboard. + /// + /// A score processor instance to handle score calculation for scores of users in the match. + public MultiplayerGameplayLeaderboard(ScoreProcessor scoreProcessor) + { + this.scoreProcessor = scoreProcessor; + + AddPlayer(new BindableDouble(), new GuestUser()); + } + + [Resolved] + private SpectatorStreamingClient streamingClient { get; set; } + + [Resolved] + private UserLookupCache userLookupCache { get; set; } + + private readonly Dictionary userScores = new Dictionary(); + + private Bindable scoringMode; + + [BackgroundDependencyLoader] + private void load(OsuConfigManager config) + { + streamingClient.OnNewFrames += handleIncomingFrames; + + foreach (var user in streamingClient.PlayingUsers) + { + streamingClient.WatchUser(user); + var resolvedUser = userLookupCache.GetUserAsync(user).Result; + + var trackedUser = new TrackedUserData(); + + userScores[user] = trackedUser; + AddPlayer(trackedUser.Score, resolvedUser); + } + + scoringMode = config.GetBindable(OsuSetting.ScoreDisplayMode); + scoringMode.BindValueChanged(updateAllScores, true); + } + + private void updateAllScores(ValueChangedEvent mode) + { + foreach (var trackedData in userScores.Values) + trackedData.UpdateScore(scoreProcessor, mode.NewValue); + } + + private void handleIncomingFrames(int userId, FrameDataBundle bundle) + { + if (userScores.TryGetValue(userId, out var trackedData)) + { + trackedData.LastHeader = bundle.Header; + trackedData.UpdateScore(scoreProcessor, scoringMode.Value); + } + } + + private class TrackedUserData + { + public readonly BindableDouble Score = new BindableDouble(); + + public readonly BindableDouble Accuracy = new BindableDouble(); + + public readonly BindableInt CurrentCombo = new BindableInt(); + + [CanBeNull] + public FrameHeader LastHeader; + + public void UpdateScore(ScoreProcessor processor, ScoringMode mode) + { + if (LastHeader == null) + return; + + (Score.Value, Accuracy.Value) = processor.GetScoreAndAccuracy(mode, LastHeader.MaxCombo, LastHeader.Statistics.ToDictionary(s => s.Result, s => s.Count)); + CurrentCombo.Value = LastHeader.Combo; + } + } + } +} From 09d0ceb7668e115170437c1d2063ed883897ed02 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 16 Dec 2020 15:26:03 +0900 Subject: [PATCH 04/60] Add testing setup to get a better visual idea of how scoreboard will work fixup! Add method to ScoreProcessor to calculate score and accuracy from statistics --- .../TestSceneSpectatorDrivenLeaderboard.cs | 109 +++++++++++++----- .../HUD/MultiplayerGameplayLeaderboard.cs | 4 +- 2 files changed, 85 insertions(+), 28 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorDrivenLeaderboard.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorDrivenLeaderboard.cs index 7211755ba6..e7ebf6e92c 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorDrivenLeaderboard.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorDrivenLeaderboard.cs @@ -2,14 +2,18 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; +using System.Threading.Tasks; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; -using osu.Game.Database; -using osu.Game.Online.API; +using osu.Framework.Utils; using osu.Game.Online.Spectator; +using osu.Game.Replays.Legacy; using osu.Game.Rulesets.Osu.Scoring; +using osu.Game.Rulesets.Scoring; +using osu.Game.Scoring; using osu.Game.Screens.Play.HUD; namespace osu.Game.Tests.Visual.Gameplay @@ -17,7 +21,7 @@ namespace osu.Game.Tests.Visual.Gameplay public class TestSceneSpectatorDrivenLeaderboard : OsuTestScene { [Cached(typeof(SpectatorStreamingClient))] - private TestSceneSpectator.TestSpectatorStreamingClient testSpectatorStreamingClient = new TestSceneSpectator.TestSpectatorStreamingClient(); + private TestMultiplayerStreaming streamingClient = new TestMultiplayerStreaming(16); // used just to show beatmap card for the time being. protected override bool UseOnlineAPI => true; @@ -27,7 +31,7 @@ namespace osu.Game.Tests.Visual.Gameplay { OsuScoreProcessor scoreProcessor; - testSpectatorStreamingClient.StartPlay(55); + streamingClient.Start(Beatmap.Value.BeatmapInfo.OnlineBeatmapID ?? 0); Children = new Drawable[] { @@ -38,37 +42,90 @@ namespace osu.Game.Tests.Visual.Gameplay Origin = Anchor.Centre, } }; + + Beatmap.Value = CreateWorkingBeatmap(Ruleset.Value); + + var playable = Beatmap.Value.GetPlayableBeatmap(Ruleset.Value); + + scoreProcessor.ApplyBeatmap(playable); }); - } - public class MultiplayerGameplayLeaderboard : GameplayLeaderboard - { - private readonly OsuScoreProcessor scoreProcessor; - - public MultiplayerGameplayLeaderboard(OsuScoreProcessor scoreProcessor) + [Test] + public void TestScoreUpdates() { - this.scoreProcessor = scoreProcessor; - - AddPlayer(new BindableDouble(), new GuestUser()); + AddRepeatStep("update state", () => streamingClient.RandomlyUpdateState(), 100); } - [Resolved] - private SpectatorStreamingClient streamingClient { get; set; } - - [Resolved] - private UserLookupCache userLookupCache { get; set; } - - [BackgroundDependencyLoader] - private void load() + public class TestMultiplayerStreaming : SpectatorStreamingClient { - Console.WriteLine("got here"); + public new BindableList PlayingUsers => (BindableList)base.PlayingUsers; - foreach (var user in streamingClient.PlayingUsers) + private readonly int totalUsers; + + public TestMultiplayerStreaming(int totalUsers) { - streamingClient.WatchUser(user); - var resolvedUser = userLookupCache.GetUserAsync(user).Result; - AddPlayer(new BindableDouble(), resolvedUser); + this.totalUsers = totalUsers; } + + public void Start(int beatmapId) + { + for (int i = 0; i < totalUsers; i++) + { + ((ISpectatorClient)this).UserBeganPlaying(i, new SpectatorState + { + BeatmapID = beatmapId, + RulesetID = 0, + }); + } + } + + private readonly Dictionary lastHeaders = new Dictionary(); + + public void RandomlyUpdateState() + { + foreach (var userId in PlayingUsers) + { + if (RNG.Next(0, 1) == 1) + continue; + + if (!lastHeaders.TryGetValue(userId, out var header)) + { + lastHeaders[userId] = header = new FrameHeader(new ScoreInfo + { + Statistics = new Dictionary(new[] + { + new KeyValuePair(HitResult.Miss, 0), + new KeyValuePair(HitResult.Meh, 0), + new KeyValuePair(HitResult.Great, 0) + }) + }); + } + + switch (RNG.Next(0, 3)) + { + case 0: + header.Combo = 0; + header.Statistics[HitResult.Miss]++; + break; + + case 1: + header.Combo++; + header.MaxCombo = Math.Max(header.MaxCombo, header.Combo); + header.Statistics[HitResult.Meh]++; + break; + + default: + header.Combo++; + header.MaxCombo = Math.Max(header.MaxCombo, header.Combo); + header.Statistics[HitResult.Great]++; + break; + } + + ((ISpectatorClient)this).UserSentFrames(userId, new FrameDataBundle(header, Array.Empty())); + } + } + + protected override Task Connect() => Task.CompletedTask; } } } diff --git a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs index 1d9fdd9ded..b3621f42c2 100644 --- a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.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 JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -90,7 +89,8 @@ namespace osu.Game.Screens.Play.HUD if (LastHeader == null) return; - (Score.Value, Accuracy.Value) = processor.GetScoreAndAccuracy(mode, LastHeader.MaxCombo, LastHeader.Statistics.ToDictionary(s => s.Result, s => s.Count)); + (Score.Value, Accuracy.Value) = processor.GetScoreAndAccuracy(mode, LastHeader.MaxCombo, LastHeader.Statistics); + CurrentCombo.Value = LastHeader.Combo; } } From c1ba0f46425e756177e6c383d7f2e42b9d176c80 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 16 Dec 2020 15:53:05 +0900 Subject: [PATCH 05/60] Use a local lookup cache for better usernames --- .../TestSceneSpectatorDrivenLeaderboard.cs | 7 +++++ .../TestSceneCurrentlyPlayingDisplay.cs | 28 ++++++++++++++++++- 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorDrivenLeaderboard.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorDrivenLeaderboard.cs index e7ebf6e92c..647d57b5fe 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorDrivenLeaderboard.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorDrivenLeaderboard.cs @@ -9,12 +9,14 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Utils; +using osu.Game.Database; using osu.Game.Online.Spectator; using osu.Game.Replays.Legacy; using osu.Game.Rulesets.Osu.Scoring; using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; using osu.Game.Screens.Play.HUD; +using osu.Game.Tests.Visual.Online; namespace osu.Game.Tests.Visual.Gameplay { @@ -23,6 +25,9 @@ namespace osu.Game.Tests.Visual.Gameplay [Cached(typeof(SpectatorStreamingClient))] private TestMultiplayerStreaming streamingClient = new TestMultiplayerStreaming(16); + [Cached(typeof(UserLookupCache))] + private UserLookupCache lookupCache = new TestSceneCurrentlyPlayingDisplay.TestUserLookupCache(); + // used just to show beatmap card for the time being. protected override bool UseOnlineAPI => true; @@ -35,6 +40,8 @@ namespace osu.Game.Tests.Visual.Gameplay Children = new Drawable[] { + streamingClient, + lookupCache, scoreProcessor = new OsuScoreProcessor(), new MultiplayerGameplayLeaderboard(scoreProcessor) { diff --git a/osu.Game.Tests/Visual/Online/TestSceneCurrentlyPlayingDisplay.cs b/osu.Game.Tests/Visual/Online/TestSceneCurrentlyPlayingDisplay.cs index 7eba64f418..4f0ca67e64 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneCurrentlyPlayingDisplay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneCurrentlyPlayingDisplay.cs @@ -69,8 +69,34 @@ namespace osu.Game.Tests.Visual.Online internal class TestUserLookupCache : UserLookupCache { + private static readonly string[] usernames = + { + "fieryrage", + "Kerensa", + "MillhioreF", + "Player01", + "smoogipoo", + "Ephemeral", + "BTMC", + "Cilvery", + "m980", + "HappyStick", + "LittleEndu", + "frenzibyte", + "Zallius", + "BanchoBot", + "rocketminer210", + "pishifat" + }; + + private int id; + protected override Task ComputeValueAsync(int lookup, CancellationToken token = default) - => Task.FromResult(new User { Username = "peppy", Id = 2 }); + => Task.FromResult(new User + { + Id = id++, + Username = usernames[id % usernames.Length], + }); } } } From 6e2131c164ad2a2d45ec7bab53d58deae3279de7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 16 Dec 2020 15:53:15 +0900 Subject: [PATCH 06/60] Don't track local user score in any special way --- osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs index b3621f42c2..f8fce0825f 100644 --- a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs @@ -7,7 +7,6 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Game.Configuration; using osu.Game.Database; -using osu.Game.Online.API; using osu.Game.Online.Spectator; using osu.Game.Rulesets.Scoring; @@ -24,8 +23,6 @@ namespace osu.Game.Screens.Play.HUD public MultiplayerGameplayLeaderboard(ScoreProcessor scoreProcessor) { this.scoreProcessor = scoreProcessor; - - AddPlayer(new BindableDouble(), new GuestUser()); } [Resolved] From 6bce587b599c04194589c8f3bd2716a44f886ea3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 16 Dec 2020 16:05:46 +0900 Subject: [PATCH 07/60] Pass users in via constructor and correctly unbind on disposal --- .../TestSceneSpectatorDrivenLeaderboard.cs | 3 +- .../HUD/MultiplayerGameplayLeaderboard.cs | 32 ++++++++++++++++--- 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorDrivenLeaderboard.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorDrivenLeaderboard.cs index 647d57b5fe..ffd02d247a 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorDrivenLeaderboard.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorDrivenLeaderboard.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using NUnit.Framework; using osu.Framework.Allocation; @@ -43,7 +44,7 @@ namespace osu.Game.Tests.Visual.Gameplay streamingClient, lookupCache, scoreProcessor = new OsuScoreProcessor(), - new MultiplayerGameplayLeaderboard(scoreProcessor) + new MultiplayerGameplayLeaderboard(scoreProcessor, streamingClient.PlayingUsers.ToArray()) { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs index f8fce0825f..93f258c507 100644 --- a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs @@ -16,13 +16,22 @@ namespace osu.Game.Screens.Play.HUD { private readonly ScoreProcessor scoreProcessor; + private readonly int[] userIds; + + private readonly Dictionary userScores = new Dictionary(); + /// /// Construct a new leaderboard. /// /// A score processor instance to handle score calculation for scores of users in the match. - public MultiplayerGameplayLeaderboard(ScoreProcessor scoreProcessor) + /// IDs of all users in this match. + public MultiplayerGameplayLeaderboard(ScoreProcessor scoreProcessor, int[] userIds) { + // 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. + this.userIds = userIds; } [Resolved] @@ -31,8 +40,6 @@ namespace osu.Game.Screens.Play.HUD [Resolved] private UserLookupCache userLookupCache { get; set; } - private readonly Dictionary userScores = new Dictionary(); - private Bindable scoringMode; [BackgroundDependencyLoader] @@ -40,9 +47,11 @@ namespace osu.Game.Screens.Play.HUD { streamingClient.OnNewFrames += handleIncomingFrames; - foreach (var user in streamingClient.PlayingUsers) + foreach (var user in userIds) { streamingClient.WatchUser(user); + + // probably won't be required in the final implementation. var resolvedUser = userLookupCache.GetUserAsync(user).Result; var trackedUser = new TrackedUserData(); @@ -70,6 +79,21 @@ namespace osu.Game.Screens.Play.HUD } } + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (streamingClient != null) + { + foreach (var user in userIds) + { + streamingClient.StopWatchingUser(user); + } + + streamingClient.OnNewFrames -= handleIncomingFrames; + } + } + private class TrackedUserData { public readonly BindableDouble Score = new BindableDouble(); From a01bb3d5a3a7ee33650de9b305addf61a1daa16f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 16 Dec 2020 16:08:44 +0900 Subject: [PATCH 08/60] Better limit bindable exposure of data class --- osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs | 2 +- .../Play/HUD/MultiplayerGameplayLeaderboard.cs | 16 +++++++++++----- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs index e53c56b390..3934a99221 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs @@ -31,7 +31,7 @@ namespace osu.Game.Screens.Play.HUD /// /// The bindable current score of the player. /// The player. - public void AddPlayer([NotNull] BindableDouble currentScore, [NotNull] User user) + public void AddPlayer([NotNull] IBindableNumber currentScore, [NotNull] User user) { var scoreItem = addScore(currentScore.Value, user); currentScore.ValueChanged += s => scoreItem.TotalScore = s.NewValue; diff --git a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs index 93f258c507..33bcf06aa7 100644 --- a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs @@ -96,11 +96,17 @@ namespace osu.Game.Screens.Play.HUD private class TrackedUserData { - public readonly BindableDouble Score = new BindableDouble(); + public IBindableNumber Score => score; - public readonly BindableDouble Accuracy = new BindableDouble(); + private readonly BindableDouble score = new BindableDouble(); - public readonly BindableInt CurrentCombo = new BindableInt(); + public IBindableNumber Accuracy => accuracy; + + private readonly BindableDouble accuracy = new BindableDouble(); + + public IBindableNumber CurrentCombo => currentCombo; + + private readonly BindableInt currentCombo = new BindableInt(); [CanBeNull] public FrameHeader LastHeader; @@ -110,9 +116,9 @@ namespace osu.Game.Screens.Play.HUD if (LastHeader == null) return; - (Score.Value, Accuracy.Value) = processor.GetScoreAndAccuracy(mode, LastHeader.MaxCombo, LastHeader.Statistics); + (score.Value, accuracy.Value) = processor.GetScoreAndAccuracy(mode, LastHeader.MaxCombo, LastHeader.Statistics); - CurrentCombo.Value = LastHeader.Combo; + currentCombo.Value = LastHeader.Combo; } } } From cda3bd2017180391786a46ef771c4b3e04dc4cb1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 16 Dec 2020 16:22:47 +0900 Subject: [PATCH 09/60] Rename test scene to match tested class name --- ...erboard.cs => TestSceneMultiplayerGameplayLeaderboard.cs} | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) rename osu.Game.Tests/Visual/Gameplay/{TestSceneSpectatorDrivenLeaderboard.cs => TestSceneMultiplayerGameplayLeaderboard.cs} (96%) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorDrivenLeaderboard.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneMultiplayerGameplayLeaderboard.cs similarity index 96% rename from osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorDrivenLeaderboard.cs rename to osu.Game.Tests/Visual/Gameplay/TestSceneMultiplayerGameplayLeaderboard.cs index ffd02d247a..78f9db57be 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorDrivenLeaderboard.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneMultiplayerGameplayLeaderboard.cs @@ -21,7 +21,7 @@ using osu.Game.Tests.Visual.Online; namespace osu.Game.Tests.Visual.Gameplay { - public class TestSceneSpectatorDrivenLeaderboard : OsuTestScene + public class TestSceneMultiplayerGameplayLeaderboard : OsuTestScene { [Cached(typeof(SpectatorStreamingClient))] private TestMultiplayerStreaming streamingClient = new TestMultiplayerStreaming(16); @@ -29,9 +29,6 @@ namespace osu.Game.Tests.Visual.Gameplay [Cached(typeof(UserLookupCache))] private UserLookupCache lookupCache = new TestSceneCurrentlyPlayingDisplay.TestUserLookupCache(); - // used just to show beatmap card for the time being. - protected override bool UseOnlineAPI => true; - [SetUp] public void SetUp() => Schedule(() => { From 41d8b84bd7c7aff099593a034c8dd71ee1d0ae13 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 17 Dec 2020 15:47:20 +0900 Subject: [PATCH 10/60] Revert MaxBaseScore to being a private field (no longe required to be public) --- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index b4f29d7a6e..f4850d3325 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -72,7 +72,7 @@ namespace osu.Game.Rulesets.Scoring /// /// The maximum achievable base score. /// - public double MaxBaseScore { get; private set; } + private double maxBaseScore; private double rollingMaxBaseScore; private double baseScore; @@ -201,7 +201,7 @@ namespace osu.Game.Rulesets.Scoring private double getScore(ScoringMode mode) { return GetScore(mode, maxAchievableCombo, - MaxBaseScore > 0 ? baseScore / MaxBaseScore : 0, + maxBaseScore > 0 ? baseScore / maxBaseScore : 0, maxAchievableCombo > 0 ? (double)HighestCombo.Value / maxAchievableCombo : 1, scoreResultCounts); } @@ -252,7 +252,7 @@ namespace osu.Game.Rulesets.Scoring computedBaseScore += Judgement.ToNumericResult(pair.Key) * pair.Value; } - double accuracy = MaxBaseScore > 0 ? computedBaseScore / MaxBaseScore : 0; + double accuracy = maxBaseScore > 0 ? computedBaseScore / maxBaseScore : 0; double comboRatio = maxAchievableCombo > 0 ? (double)HighestCombo.Value / maxAchievableCombo : 1; double score = GetScore(mode, maxAchievableCombo, accuracy, comboRatio, scoreResultCounts); @@ -299,7 +299,7 @@ namespace osu.Game.Rulesets.Scoring if (storeResults) { maxAchievableCombo = HighestCombo.Value; - MaxBaseScore = baseScore; + maxBaseScore = baseScore; } baseScore = 0; From de9c21e7d19e265fbf776430d2708f860ed16bbf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 17 Dec 2020 15:48:53 +0900 Subject: [PATCH 11/60] Tenatively mark leaderboard class as LongRunningLoad until final integration --- osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs index 33bcf06aa7..b2dc47ce61 100644 --- a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs @@ -12,6 +12,7 @@ using osu.Game.Rulesets.Scoring; namespace osu.Game.Screens.Play.HUD { + [LongRunningLoad] public class MultiplayerGameplayLeaderboard : GameplayLeaderboard { private readonly ScoreProcessor scoreProcessor; From cc3dddf59fa432bad0fc2d36fe8810853223f48f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 17 Dec 2020 16:02:06 +0900 Subject: [PATCH 12/60] Fix test scene crashing on second run of SetUp Also correctly support LongRunningLoad --- ...TestSceneMultiplayerGameplayLeaderboard.cs | 53 +++++++++++++------ 1 file changed, 36 insertions(+), 17 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneMultiplayerGameplayLeaderboard.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneMultiplayerGameplayLeaderboard.cs index 78f9db57be..0c8d8ca165 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneMultiplayerGameplayLeaderboard.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneMultiplayerGameplayLeaderboard.cs @@ -9,6 +9,8 @@ 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.Database; using osu.Game.Online.Spectator; @@ -29,31 +31,48 @@ namespace osu.Game.Tests.Visual.Gameplay [Cached(typeof(UserLookupCache))] private UserLookupCache lookupCache = new TestSceneCurrentlyPlayingDisplay.TestUserLookupCache(); - [SetUp] - public void SetUp() => Schedule(() => + private MultiplayerGameplayLeaderboard leaderboard; + + protected override Container Content { get; } = new Container { RelativeSizeAxes = Axes.Both }; + + public TestSceneMultiplayerGameplayLeaderboard() { - OsuScoreProcessor scoreProcessor; - - streamingClient.Start(Beatmap.Value.BeatmapInfo.OnlineBeatmapID ?? 0); - - Children = new Drawable[] + base.Content.Children = new Drawable[] { streamingClient, lookupCache, - scoreProcessor = new OsuScoreProcessor(), - new MultiplayerGameplayLeaderboard(scoreProcessor, streamingClient.PlayingUsers.ToArray()) + Content + }; + } + + [SetUpSteps] + public void SetUpSteps() + { + AddStep("create leaderboard", () => + { + OsuScoreProcessor scoreProcessor; + Beatmap.Value = CreateWorkingBeatmap(Ruleset.Value); + + var playable = Beatmap.Value.GetPlayableBeatmap(Ruleset.Value); + + streamingClient.Start(Beatmap.Value.BeatmapInfo.OnlineBeatmapID ?? 0); + + Children = new Drawable[] + { + scoreProcessor = new OsuScoreProcessor(), + }; + + scoreProcessor.ApplyBeatmap(playable); + + LoadComponentAsync(leaderboard = new MultiplayerGameplayLeaderboard(scoreProcessor, streamingClient.PlayingUsers.ToArray()) { Anchor = Anchor.Centre, Origin = Anchor.Centre, - } - }; + }, Add); + }); - Beatmap.Value = CreateWorkingBeatmap(Ruleset.Value); - - var playable = Beatmap.Value.GetPlayableBeatmap(Ruleset.Value); - - scoreProcessor.ApplyBeatmap(playable); - }); + AddUntilStep("wait for load", () => leaderboard.IsLoaded); + } [Test] public void TestScoreUpdates() From f13683dc908e2f42b19d2a1fb6b05f808eb01b36 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 17 Dec 2020 16:05:41 +0900 Subject: [PATCH 13/60] Correctly account for max combo of the input, rather than the global --- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index f4850d3325..f5fb918ba3 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -253,7 +253,7 @@ namespace osu.Game.Rulesets.Scoring } double accuracy = maxBaseScore > 0 ? computedBaseScore / maxBaseScore : 0; - double comboRatio = maxAchievableCombo > 0 ? (double)HighestCombo.Value / maxAchievableCombo : 1; + double comboRatio = maxAchievableCombo > 0 ? (double)maxCombo / maxAchievableCombo : 1; double score = GetScore(mode, maxAchievableCombo, accuracy, comboRatio, scoreResultCounts); From 0faf3fdfd3fc4f96d137d1e8824398383e441ace Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 17 Dec 2020 15:12:32 +0300 Subject: [PATCH 14/60] Update gameplay leaderboard scores with the new design --- .../Gameplay/TestSceneGameplayLeaderboard.cs | 11 +- .../Screens/Play/HUD/GameplayLeaderboard.cs | 47 +-- .../Play/HUD/GameplayLeaderboardScore.cs | 268 ++++++++++++------ 3 files changed, 215 insertions(+), 111 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs index df970c1c46..d0fdb3dd9c 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs @@ -24,9 +24,8 @@ namespace osu.Game.Tests.Visual.Gameplay Add(leaderboard = new TestGameplayLeaderboard { Anchor = Anchor.Centre, - Origin = Anchor.Centre, + Origin = Anchor.TopCentre, Scale = new Vector2(2), - RelativeSizeAxes = Axes.X, }); } @@ -39,7 +38,7 @@ namespace osu.Game.Tests.Visual.Gameplay playerScore.Value = 1222333; }); - AddStep("add player user", () => leaderboard.AddPlayer(playerScore, new User { Username = "You" })); + AddStep("add player user", () => leaderboard.AddLocalUser(playerScore, new User { Username = "You" })); AddSliderStep("set player score", 50, 5000000, 1222333, v => playerScore.Value = v); } @@ -75,6 +74,12 @@ namespace osu.Game.Tests.Visual.Gameplay return scoreItem != null && scoreItem.ScorePosition == expectedPosition; } + + public void AddPlayer(BindableDouble totalScore, User user) => + base.AddPlayer(totalScore, new BindableDouble(1f), new BindableInt(1), user, false); + + public void AddLocalUser(BindableDouble totalScore, User user) => + base.AddPlayer(totalScore, new BindableDouble(1f), new BindableInt(1), user, true); } } } diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs index e53c56b390..a2673e3097 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs @@ -15,8 +15,7 @@ namespace osu.Game.Screens.Play.HUD { public GameplayLeaderboard() { - RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; + AutoSizeAxes = Axes.Both; Direction = FillDirection.Vertical; @@ -29,32 +28,44 @@ namespace osu.Game.Screens.Play.HUD /// /// Adds a player to the leaderboard. /// - /// The bindable current score of the player. + /// A bindable of the player's total score. + /// A bindable of the player's accuracy. + /// A bindable of the player's current combo. /// The player. - public void AddPlayer([NotNull] BindableDouble currentScore, [NotNull] User user) + public void AddPlayer([NotNull] IBindableNumber totalScore, + [NotNull] IBindableNumber accuracy, + [NotNull] IBindableNumber combo, + [NotNull] User user) { - var scoreItem = addScore(currentScore.Value, user); - currentScore.ValueChanged += s => scoreItem.TotalScore = s.NewValue; + AddPlayer(totalScore, accuracy, combo, user, false); } - private GameplayLeaderboardScore addScore(double totalScore, User user) + /// + /// Adds a player to the leaderboard. + /// + /// A bindable of the player's total score. + /// A bindable of the player's accuracy. + /// A bindable of the player's current combo. + /// The player. + /// Whether the provided is the local user. + protected void AddPlayer([NotNull] IBindableNumber totalScore, + [NotNull] IBindableNumber accuracy, + [NotNull] IBindableNumber combo, + [NotNull] User user, bool localUser) => Schedule(() => { - var scoreItem = new GameplayLeaderboardScore + Add(new GameplayLeaderboardScore(user, localUser) { - User = user, - TotalScore = totalScore, - OnScoreChange = updateScores, - }; + TotalScore = { BindTarget = totalScore }, + Accuracy = { BindTarget = accuracy }, + Combo = { BindTarget = combo }, + }); - Add(scoreItem); - updateScores(); - - return scoreItem; - } + totalScore.BindValueChanged(_ => updateScores(), true); + }); private void updateScores() { - var orderedByScore = this.OrderByDescending(i => i.TotalScore).ToList(); + var orderedByScore = this.OrderByDescending(i => i.TotalScore.Value).ToList(); for (int i = 0; i < Count; i++) { diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs index 4c75f422c9..62ea5782e8 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs @@ -1,25 +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 System.Linq; using Humanizer; 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.Game.Graphics; using osu.Game.Graphics.Sprites; +using osu.Game.Online.API; using osu.Game.Users; +using osu.Game.Utils; using osuTK; +using osuTK.Graphics; namespace osu.Game.Screens.Play.HUD { public class GameplayLeaderboardScore : CompositeDrawable { - private readonly OsuSpriteText positionText, positionSymbol, userString; - private readonly GlowingSpriteText scoreText; + private const float regular_width = 215f; + private const float extended_width = 235f; - public Action OnScoreChange; + private const float panel_height = 35f; + + private OsuSpriteText positionText, scoreText, accuracyText, comboText; + public readonly BindableDouble TotalScore = new BindableDouble(); + public readonly BindableDouble Accuracy = new BindableDouble(); + public readonly BindableInt Combo = new BindableInt(); private int? scorePosition; @@ -34,103 +44,181 @@ namespace osu.Game.Screens.Play.HUD positionText.Text = $"#{scorePosition.Value.ToMetric(decimals: scorePosition < 100000 ? 1 : 0)}"; positionText.FadeTo(scorePosition.HasValue ? 1 : 0); - positionSymbol.FadeTo(scorePosition.HasValue ? 1 : 0); } } - private double totalScore; + public User User { get; } - public double TotalScore + private readonly bool localUser; + + public GameplayLeaderboardScore(User user, bool localUser) { - get => totalScore; - set - { - totalScore = value; - scoreText.Text = totalScore.ToString("N0"); + User = user; + this.localUser = localUser; - OnScoreChange?.Invoke(); - } - } + AutoSizeAxes = Axes.Both; - private User user; - - public User User - { - get => user; - set - { - user = value; - userString.Text = user?.Username; - } - } - - public GameplayLeaderboardScore() - { - RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; - - InternalChild = new Container - { - Masking = true, - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - AutoSizeAxes = Axes.Both, - Children = new Drawable[] - { - new FillFlowContainer - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopRight, - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Margin = new MarginPadding { Right = 2.5f }, - Spacing = new Vector2(2.5f), - Children = new[] - { - positionText = new OsuSpriteText - { - Alpha = 0, - Font = OsuFont.GetFont(size: 14, weight: FontWeight.Bold), - }, - positionSymbol = new OsuSpriteText - { - Alpha = 0, - Font = OsuFont.GetFont(size: 14, weight: FontWeight.SemiBold), - Text = ">", - }, - } - }, - new FillFlowContainer - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopLeft, - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Margin = new MarginPadding { Left = 2.5f }, - Spacing = new Vector2(2.5f), - Children = new Drawable[] - { - userString = new OsuSpriteText - { - Size = new Vector2(80, 16), - Font = OsuFont.GetFont(size: 14, weight: FontWeight.Bold, italics: true), - }, - scoreText = new GlowingSpriteText - { - GlowColour = Color4Extensions.FromHex(@"83ccfa"), - Font = OsuFont.Numeric.With(size: 14), - } - } - }, - }, - }; + Anchor = Anchor.TopRight; + Origin = Anchor.TopRight; } [BackgroundDependencyLoader] - private void load(OsuColour colours) + private void load(IAPIProvider api) { - positionText.Colour = colours.YellowLight; - positionSymbol.Colour = colours.Yellow; + const float panel_shear = 0.15f; + const float shear_width = panel_height * panel_shear; + + Color4 panelColour, textColour; + float panelWidth; + + if (localUser) + { + panelWidth = extended_width; + panelColour = Color4Extensions.FromHex("7fcc33"); + textColour = Color4.White; + } + else if (api.Friends.Any(f => User.Equals(f))) + { + panelWidth = extended_width; + panelColour = Color4Extensions.FromHex("ffd966"); + textColour = Color4Extensions.FromHex("2e576b"); + } + else + { + panelWidth = regular_width; + panelColour = Color4Extensions.FromHex("3399cc"); + textColour = Color4.White; + } + + InternalChildren = new Drawable[] + { + new Container + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Masking = true, + CornerRadius = 5f, + Shear = new Vector2(panel_shear, 0f), + Size = new Vector2(panelWidth, panel_height), + Child = new Box + { + Alpha = 0.5f, + RelativeSizeAxes = Axes.Both, + Colour = panelColour, + } + }, + new GridContainer + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Size = new Vector2(regular_width, panel_height), + ColumnDimensions = new[] + { + new Dimension(GridSizeMode.Absolute, 35f), + new Dimension(), + new Dimension(GridSizeMode.Absolute, 85f), + }, + Content = new[] + { + new Drawable[] + { + positionText = new OsuSpriteText + { + Padding = new MarginPadding { Right = shear_width / 2 }, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Colour = textColour, + Font = OsuFont.Torus.With(size: 14, weight: FontWeight.Bold), + Shadow = false, + }, + new Container + { + Padding = new MarginPadding { Horizontal = shear_width / 3 }, + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + new Container + { + Masking = true, + CornerRadius = 5f, + Shear = new Vector2(panel_shear, 0f), + RelativeSizeAxes = Axes.Both, + Children = new[] + { + new Box + { + Alpha = 0.5f, + RelativeSizeAxes = Axes.Both, + Colour = panelColour, + }, + } + }, + new OsuSpriteText + { + Padding = new MarginPadding { Left = shear_width }, + RelativeSizeAxes = Axes.X, + Width = 0.8f, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Colour = textColour, + Font = OsuFont.Torus.With(size: 14, weight: FontWeight.SemiBold), + Text = User.Username, + Truncate = true, + Shadow = false, + } + } + }, + new Container + { + Padding = new MarginPadding { Top = 2f, Right = 17.5f, Bottom = 5f }, + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Colour = textColour, + Children = new Drawable[] + { + scoreText = new OsuSpriteText + { + Anchor = Anchor.TopLeft, + Origin = Anchor.TopLeft, + Spacing = new Vector2(0.5f, 0f), + Font = OsuFont.Torus.With(size: 16, weight: FontWeight.SemiBold), + Shadow = false, + }, + new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Children = new Drawable[] + { + accuracyText = new OsuSpriteText + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Font = OsuFont.Torus.With(size: 12, weight: FontWeight.SemiBold), + Shadow = false, + }, + comboText = new OsuSpriteText + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + Font = OsuFont.Torus.With(size: 12, weight: FontWeight.SemiBold), + Shadow = false, + }, + } + } + }, + } + } + } + } + }; + + TotalScore.BindValueChanged(v => scoreText.Text = v.NewValue.ToString("N0"), true); + Accuracy.BindValueChanged(v => accuracyText.Text = v.NewValue.FormatAccuracy(), true); + Combo.BindValueChanged(v => comboText.Text = $"{v.NewValue}x", true); } } } From a01ed1827a481fb8ac87aef1a1fda89ed5b33f4e Mon Sep 17 00:00:00 2001 From: Graham Johnson Date: Thu, 17 Dec 2020 19:34:16 -0500 Subject: [PATCH 15/60] Align the drag circles on the selction box in the editor to be on the center of the border --- .../Edit/Compose/Components/SelectionBox.cs | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs index 347d9e3ba7..e4feceb987 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs @@ -235,6 +235,8 @@ namespace osu.Game.Screens.Edit.Compose.Components private void addDragHandle(Anchor anchor) => AddInternal(new SelectionBoxDragHandle { Anchor = anchor, + Y = getAdjustmentToCenterCircleOnBorder(anchor).Y, + X = getAdjustmentToCenterCircleOnBorder(anchor).X, HandleDrag = e => OnScale?.Invoke(e.Delta, anchor), OperationStarted = operationStarted, OperationEnded = operationEnded @@ -251,6 +253,45 @@ namespace osu.Game.Screens.Edit.Compose.Components return (endAngle - startAngle) * 180 / MathF.PI; } + /// + /// Adjust Drag circle to be centered on the center of the border instead of on the edge. + /// + /// The part of the rectangle to be adjusted. + private Vector2 getAdjustmentToCenterCircleOnBorder(Anchor anchor) + { + Vector2 adjustment = Vector2.Zero; + + switch (anchor) + { + case Anchor.TopLeft: + case Anchor.CentreLeft: + case Anchor.BottomLeft: + adjustment.X = BORDER_RADIUS / 2; + break; + case Anchor.TopRight: + case Anchor.CentreRight: + case Anchor.BottomRight: + adjustment.X = -BORDER_RADIUS / 2; + break; + } + + switch (anchor) + { + case Anchor.TopLeft: + case Anchor.TopCentre: + case Anchor.TopRight: + adjustment.Y = BORDER_RADIUS / 2; + break; + case Anchor.BottomLeft: + case Anchor.BottomCentre: + case Anchor.BottomRight: + adjustment.Y = -BORDER_RADIUS / 2; + break; + } + + return adjustment; + } + private void operationEnded() { if (--activeOperations == 0) From a8abefcd665adf869b97e45d89c0d85a6d979a7b Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 18 Dec 2020 03:34:33 +0300 Subject: [PATCH 16/60] Make GameplayLeaderboardScore a model class --- .../Gameplay/TestSceneGameplayLeaderboard.cs | 20 +++++---- .../Screens/Play/HUD/GameplayLeaderboard.cs | 41 ++----------------- .../Play/HUD/GameplayLeaderboardScore.cs | 5 ++- 3 files changed, 17 insertions(+), 49 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs index d0fdb3dd9c..12bc918d45 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs @@ -38,7 +38,7 @@ namespace osu.Game.Tests.Visual.Gameplay playerScore.Value = 1222333; }); - AddStep("add player user", () => leaderboard.AddLocalUser(playerScore, new User { Username = "You" })); + AddStep("add local player", () => leaderboard.Add(createLeaderboardScore(playerScore, "You", true))); AddSliderStep("set player score", 50, 5000000, 1222333, v => playerScore.Value = v); } @@ -48,8 +48,8 @@ namespace osu.Game.Tests.Visual.Gameplay var player2Score = new BindableDouble(1234567); var player3Score = new BindableDouble(1111111); - AddStep("add player 2", () => leaderboard.AddPlayer(player2Score, new User { Username = "Player 2" })); - AddStep("add player 3", () => leaderboard.AddPlayer(player3Score, new User { Username = "Player 3" })); + AddStep("add player 2", () => leaderboard.Add(createLeaderboardScore(player2Score, "Player 2"))); + AddStep("add player 3", () => leaderboard.Add(createLeaderboardScore(player3Score, "Player 3"))); AddAssert("is player 2 position #1", () => leaderboard.CheckPositionByUsername("Player 2", 1)); AddAssert("is player position #2", () => leaderboard.CheckPositionByUsername("You", 2)); @@ -66,6 +66,14 @@ namespace osu.Game.Tests.Visual.Gameplay AddAssert("is player 2 position #3", () => leaderboard.CheckPositionByUsername("Player 2", 3)); } + private static GameplayLeaderboardScore createLeaderboardScore(BindableDouble score, string username, bool localOrReplayPlayer = false) + { + return new GameplayLeaderboardScore(new User { Username = username }, localOrReplayPlayer) + { + TotalScore = { BindTarget = score }, + }; + } + private class TestGameplayLeaderboard : GameplayLeaderboard { public bool CheckPositionByUsername(string username, int? expectedPosition) @@ -74,12 +82,6 @@ namespace osu.Game.Tests.Visual.Gameplay return scoreItem != null && scoreItem.ScorePosition == expectedPosition; } - - public void AddPlayer(BindableDouble totalScore, User user) => - base.AddPlayer(totalScore, new BindableDouble(1f), new BindableInt(1), user, false); - - public void AddLocalUser(BindableDouble totalScore, User user) => - base.AddPlayer(totalScore, new BindableDouble(1f), new BindableInt(1), user, true); } } } diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs index a2673e3097..573bf54b14 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs @@ -2,11 +2,8 @@ // See the LICENCE file in the repository root for full licence text. using System.Linq; -using JetBrains.Annotations; -using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Game.Users; using osuTK; namespace osu.Game.Screens.Play.HUD @@ -25,44 +22,12 @@ namespace osu.Game.Screens.Play.HUD LayoutEasing = Easing.OutQuint; } - /// - /// Adds a player to the leaderboard. - /// - /// A bindable of the player's total score. - /// A bindable of the player's accuracy. - /// A bindable of the player's current combo. - /// The player. - public void AddPlayer([NotNull] IBindableNumber totalScore, - [NotNull] IBindableNumber accuracy, - [NotNull] IBindableNumber combo, - [NotNull] User user) + public override void Add(GameplayLeaderboardScore drawable) { - AddPlayer(totalScore, accuracy, combo, user, false); + base.Add(drawable); + drawable?.TotalScore.BindValueChanged(_ => updateScores(), true); } - /// - /// Adds a player to the leaderboard. - /// - /// A bindable of the player's total score. - /// A bindable of the player's accuracy. - /// A bindable of the player's current combo. - /// The player. - /// Whether the provided is the local user. - protected void AddPlayer([NotNull] IBindableNumber totalScore, - [NotNull] IBindableNumber accuracy, - [NotNull] IBindableNumber combo, - [NotNull] User user, bool localUser) => Schedule(() => - { - Add(new GameplayLeaderboardScore(user, localUser) - { - TotalScore = { BindTarget = totalScore }, - Accuracy = { BindTarget = accuracy }, - Combo = { BindTarget = combo }, - }); - - totalScore.BindValueChanged(_ => updateScores(), true); - }); - private void updateScores() { var orderedByScore = this.OrderByDescending(i => i.TotalScore.Value).ToList(); diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs index 62ea5782e8..f94d6bd01f 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs @@ -27,8 +27,9 @@ namespace osu.Game.Screens.Play.HUD private const float panel_height = 35f; private OsuSpriteText positionText, scoreText, accuracyText, comboText; - public readonly BindableDouble TotalScore = new BindableDouble(); - public readonly BindableDouble Accuracy = new BindableDouble(); + + public readonly BindableDouble TotalScore = new BindableDouble(1000000); + public readonly BindableDouble Accuracy = new BindableDouble(1); public readonly BindableInt Combo = new BindableInt(); private int? scorePosition; From 92bf74ba29b3bc6c8ec645fe54d9c218d76ff6cf Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 18 Dec 2020 03:37:24 +0300 Subject: [PATCH 17/60] localUser -> localOrReplayPlayer --- .../Screens/Play/HUD/GameplayLeaderboardScore.cs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs index f94d6bd01f..285ed93341 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs @@ -50,12 +50,17 @@ namespace osu.Game.Screens.Play.HUD public User User { get; } - private readonly bool localUser; + private readonly bool localOrReplayPlayer; - public GameplayLeaderboardScore(User user, bool localUser) + /// + /// Creates a new . + /// + /// The score's player. + /// Whether the player is the local user or a replay player. + public GameplayLeaderboardScore(User user, bool localOrReplayPlayer) { User = user; - this.localUser = localUser; + this.localOrReplayPlayer = localOrReplayPlayer; AutoSizeAxes = Axes.Both; @@ -72,7 +77,7 @@ namespace osu.Game.Screens.Play.HUD Color4 panelColour, textColour; float panelWidth; - if (localUser) + if (localOrReplayPlayer) { panelWidth = extended_width; panelColour = Color4Extensions.FromHex("7fcc33"); From a0235a06e642319ce5108c21a3643e09bdc2031f Mon Sep 17 00:00:00 2001 From: Graham Johnson Date: Thu, 17 Dec 2020 19:40:21 -0500 Subject: [PATCH 18/60] update comment --- osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs index e4feceb987..4a0a925962 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs @@ -257,6 +257,7 @@ namespace osu.Game.Screens.Edit.Compose.Components /// Adjust Drag circle to be centered on the center of the border instead of on the edge. /// /// The part of the rectangle to be adjusted. + /// A 2d vector on how much to adjust the drag circle private Vector2 getAdjustmentToCenterCircleOnBorder(Anchor anchor) { Vector2 adjustment = Vector2.Zero; From 44f4ed4fd3001587378bae5ff90d4e4edfd5011a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 18 Dec 2020 11:19:40 +0900 Subject: [PATCH 19/60] Fix spacing --- osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs index 4a0a925962..f50e599457 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs @@ -269,6 +269,7 @@ namespace osu.Game.Screens.Edit.Compose.Components case Anchor.BottomLeft: adjustment.X = BORDER_RADIUS / 2; break; + case Anchor.TopRight: case Anchor.CentreRight: case Anchor.BottomRight: @@ -283,6 +284,7 @@ namespace osu.Game.Screens.Edit.Compose.Components case Anchor.TopRight: adjustment.Y = BORDER_RADIUS / 2; break; + case Anchor.BottomLeft: case Anchor.BottomCentre: case Anchor.BottomRight: From 9079d33412207d9f3e18940d3d61c8d8f1a86137 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 18 Dec 2020 11:20:21 +0900 Subject: [PATCH 20/60] X before Y for sanity --- osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs index f50e599457..8ccd4f71e9 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs @@ -235,8 +235,8 @@ namespace osu.Game.Screens.Edit.Compose.Components private void addDragHandle(Anchor anchor) => AddInternal(new SelectionBoxDragHandle { Anchor = anchor, - Y = getAdjustmentToCenterCircleOnBorder(anchor).Y, X = getAdjustmentToCenterCircleOnBorder(anchor).X, + Y = getAdjustmentToCenterCircleOnBorder(anchor).Y, HandleDrag = e => OnScale?.Invoke(e.Delta, anchor), OperationStarted = operationStarted, OperationEnded = operationEnded From 4af508235ee855cd0ac9be9d5aed56d416651f95 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 18 Dec 2020 15:35:18 +0900 Subject: [PATCH 21/60] Rename long variable --- osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs index 285ed93341..1d548e8809 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs @@ -50,17 +50,17 @@ namespace osu.Game.Screens.Play.HUD public User User { get; } - private readonly bool localOrReplayPlayer; + private readonly bool trackedPlayer; /// /// Creates a new . /// /// The score's player. - /// Whether the player is the local user or a replay player. - public GameplayLeaderboardScore(User user, bool localOrReplayPlayer) + /// Whether the player is the local user or a replay player. + public GameplayLeaderboardScore(User user, bool trackedPlayer) { User = user; - this.localOrReplayPlayer = localOrReplayPlayer; + this.trackedPlayer = trackedPlayer; AutoSizeAxes = Axes.Both; @@ -77,7 +77,7 @@ namespace osu.Game.Screens.Play.HUD Color4 panelColour, textColour; float panelWidth; - if (localOrReplayPlayer) + if (trackedPlayer) { panelWidth = extended_width; panelColour = Color4Extensions.FromHex("7fcc33"); From c80ecec0b418eedfa54931ef34d496984655cec1 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 18 Dec 2020 15:36:24 +0900 Subject: [PATCH 22/60] Reorder methods --- osu.Game/Screens/Play/Player.cs | 42 ++++++++++++++++----------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index a54f9fc047..92c76ec2d2 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -531,29 +531,8 @@ namespace osu.Game.Screens.Play completionProgressDelegate = Schedule(GotoRanking); } - protected virtual ScoreInfo CreateScore() - { - var score = new ScoreInfo - { - Beatmap = Beatmap.Value.BeatmapInfo, - Ruleset = rulesetInfo, - Mods = Mods.Value.ToArray(), - }; - - if (DrawableRuleset.ReplayScore != null) - score.User = DrawableRuleset.ReplayScore.ScoreInfo?.User ?? new GuestUser(); - else - score.User = api.LocalUser.Value; - - ScoreProcessor.PopulateScore(score); - - return score; - } - protected override bool OnScroll(ScrollEvent e) => mouseWheelDisabled.Value && !GameplayClockContainer.IsPaused.Value; - protected virtual ResultsScreen CreateResults(ScoreInfo score) => new SoloResultsScreen(score, true); - #region Fail Logic protected FailOverlay FailOverlay { get; private set; } @@ -748,6 +727,25 @@ namespace osu.Game.Screens.Play return base.OnExiting(next); } + protected virtual ScoreInfo CreateScore() + { + var score = new ScoreInfo + { + Beatmap = Beatmap.Value.BeatmapInfo, + Ruleset = rulesetInfo, + Mods = Mods.Value.ToArray(), + }; + + if (DrawableRuleset.ReplayScore != null) + score.User = DrawableRuleset.ReplayScore.ScoreInfo?.User ?? new GuestUser(); + else + score.User = api.LocalUser.Value; + + ScoreProcessor.PopulateScore(score); + + return score; + } + protected virtual void GotoRanking() { if (DrawableRuleset.ReplayScore != null) @@ -781,6 +779,8 @@ namespace osu.Game.Screens.Play })); } + protected virtual ResultsScreen CreateResults(ScoreInfo score) => new SoloResultsScreen(score, true); + private void fadeOut(bool instant = false) { float fadeOutDuration = instant ? 0 : 250; From ceb2e4762d7c63efc3f9e044ae326447543c8528 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 18 Dec 2020 16:20:42 +0900 Subject: [PATCH 23/60] Add test covering a more consistent spread of player scores --- .../Visual/Gameplay/TestSceneGameplayLeaderboard.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs index 12bc918d45..d596def98a 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs @@ -6,6 +6,7 @@ using NUnit.Framework; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Testing; +using osu.Framework.Utils; using osu.Game.Screens.Play.HUD; using osu.Game.Users; using osuTK; @@ -66,6 +67,13 @@ namespace osu.Game.Tests.Visual.Gameplay AddAssert("is player 2 position #3", () => leaderboard.CheckPositionByUsername("Player 2", 3)); } + [Test] + public void TestRandomScores() + { + int playerNumber = 1; + AddRepeatStep("add player with random score", () => leaderboard.Add(createLeaderboardScore(new BindableDouble(RNG.Next(0, 5_000_000)), $"Player {playerNumber++}")), 10); + } + private static GameplayLeaderboardScore createLeaderboardScore(BindableDouble score, string username, bool localOrReplayPlayer = false) { return new GameplayLeaderboardScore(new User { Username = username }, localOrReplayPlayer) From c84807ed5c0f09e47993666920d5374d561c5f9b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 18 Dec 2020 16:20:54 +0900 Subject: [PATCH 24/60] Refactor implementation --- .../Play/HUD/GameplayLeaderboardScore.cs | 123 +++++++++++------- 1 file changed, 73 insertions(+), 50 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs index 1d548e8809..439210c944 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.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 Humanizer; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -11,7 +10,6 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; -using osu.Game.Online.API; using osu.Game.Users; using osu.Game.Utils; using osuTK; @@ -26,7 +24,7 @@ namespace osu.Game.Screens.Play.HUD private const float panel_height = 35f; - private OsuSpriteText positionText, scoreText, accuracyText, comboText; + private OsuSpriteText positionText, scoreText, accuracyText, comboText, usernameText; public readonly BindableDouble TotalScore = new BindableDouble(1000000); public readonly BindableDouble Accuracy = new BindableDouble(1); @@ -45,6 +43,7 @@ namespace osu.Game.Screens.Play.HUD positionText.Text = $"#{scorePosition.Value.ToMetric(decimals: scorePosition < 100000 ? 1 : 0)}"; positionText.FadeTo(scorePosition.HasValue ? 1 : 0); + updateColour(); } } @@ -52,6 +51,9 @@ namespace osu.Game.Screens.Play.HUD private readonly bool trackedPlayer; + private Container mainFillContainer; + private Box centralFill; + /// /// Creates a new . /// @@ -62,62 +64,93 @@ namespace osu.Game.Screens.Play.HUD User = user; this.trackedPlayer = trackedPlayer; - AutoSizeAxes = Axes.Both; + AutoSizeAxes = Axes.X; + Height = panel_height; Anchor = Anchor.TopRight; Origin = Anchor.TopRight; } - [BackgroundDependencyLoader] - private void load(IAPIProvider api) + protected override void LoadComplete() { - const float panel_shear = 0.15f; - const float shear_width = panel_height * panel_shear; + base.LoadComplete(); - Color4 panelColour, textColour; - float panelWidth; + updateColour(); + FinishTransforms(true); + } - if (trackedPlayer) + private void updateColour() + { + if (scorePosition == 1) { - panelWidth = extended_width; + mainFillContainer.ResizeWidthTo(extended_width, 200, Easing.OutQuint); panelColour = Color4Extensions.FromHex("7fcc33"); textColour = Color4.White; } - else if (api.Friends.Any(f => User.Equals(f))) + else if (trackedPlayer) { - panelWidth = extended_width; + mainFillContainer.ResizeWidthTo(extended_width, 200, Easing.OutQuint); panelColour = Color4Extensions.FromHex("ffd966"); textColour = Color4Extensions.FromHex("2e576b"); } else { - panelWidth = regular_width; + mainFillContainer.ResizeWidthTo(regular_width, 200, Easing.OutQuint); panelColour = Color4Extensions.FromHex("3399cc"); textColour = Color4.White; } + } + + private Color4 panelColour + { + set + { + mainFillContainer.FadeColour(value, 200, Easing.OutQuint); + centralFill.FadeColour(value, 200, Easing.OutQuint); + } + } + + private Color4 textColour + { + set + { + scoreText.FadeColour(value, 200, Easing.OutQuint); + accuracyText.FadeColour(value, 200, Easing.OutQuint); + comboText.FadeColour(value, 200, Easing.OutQuint); + usernameText.FadeColour(value, 200, Easing.OutQuint); + positionText.FadeColour(value, 200, Easing.OutQuint); + } + } + + [BackgroundDependencyLoader] + private void load() + { + const float panel_shear = 0.15f; + const float shear_width = panel_height * panel_shear; InternalChildren = new Drawable[] { - new Container + mainFillContainer = new Container { + Width = regular_width, + RelativeSizeAxes = Axes.Y, Anchor = Anchor.TopRight, Origin = Anchor.TopRight, Masking = true, CornerRadius = 5f, Shear = new Vector2(panel_shear, 0f), - Size = new Vector2(panelWidth, panel_height), Child = new Box { Alpha = 0.5f, RelativeSizeAxes = Axes.Both, - Colour = panelColour, } }, new GridContainer { + Width = regular_width, + RelativeSizeAxes = Axes.Y, Anchor = Anchor.TopRight, Origin = Anchor.TopRight, - Size = new Vector2(regular_width, panel_height), ColumnDimensions = new[] { new Dimension(GridSizeMode.Absolute, 35f), @@ -133,7 +166,7 @@ namespace osu.Game.Screens.Play.HUD Padding = new MarginPadding { Right = shear_width / 2 }, Anchor = Anchor.Centre, Origin = Anchor.Centre, - Colour = textColour, + Colour = Color4.White, Font = OsuFont.Torus.With(size: 14, weight: FontWeight.Bold), Shadow = false, }, @@ -151,22 +184,22 @@ namespace osu.Game.Screens.Play.HUD RelativeSizeAxes = Axes.Both, Children = new[] { - new Box + centralFill = new Box { Alpha = 0.5f, RelativeSizeAxes = Axes.Both, - Colour = panelColour, + Colour = Color4Extensions.FromHex("3399cc"), }, } }, - new OsuSpriteText + usernameText = new OsuSpriteText { Padding = new MarginPadding { Left = shear_width }, RelativeSizeAxes = Axes.X, Width = 0.8f, Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, - Colour = textColour, + Colour = Color4.White, Font = OsuFont.Torus.With(size: 14, weight: FontWeight.SemiBold), Text = User.Username, Truncate = true, @@ -180,41 +213,31 @@ namespace osu.Game.Screens.Play.HUD RelativeSizeAxes = Axes.Both, Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, - Colour = textColour, + Colour = Color4.White, Children = new Drawable[] { scoreText = new OsuSpriteText { - Anchor = Anchor.TopLeft, - Origin = Anchor.TopLeft, - Spacing = new Vector2(0.5f, 0f), - Font = OsuFont.Torus.With(size: 16, weight: FontWeight.SemiBold), + Spacing = new Vector2(-1f, 0f), + Font = OsuFont.Torus.With(size: 16, weight: FontWeight.SemiBold, fixedWidth: true), Shadow = false, }, - new Container + accuracyText = new OsuSpriteText { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, - Children = new Drawable[] - { - accuracyText = new OsuSpriteText - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Font = OsuFont.Torus.With(size: 12, weight: FontWeight.SemiBold), - Shadow = false, - }, - comboText = new OsuSpriteText - { - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - Font = OsuFont.Torus.With(size: 12, weight: FontWeight.SemiBold), - Shadow = false, - }, - } - } + Font = OsuFont.Torus.With(size: 12, weight: FontWeight.SemiBold, fixedWidth: true), + Spacing = new Vector2(-1f, 0f), + Shadow = false, + }, + comboText = new OsuSpriteText + { + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, + Spacing = new Vector2(-1f, 0f), + Font = OsuFont.Torus.With(size: 12, weight: FontWeight.SemiBold, fixedWidth: true), + Shadow = false, + }, }, } } From 07a8ffa4aaefeeaa2da30aea60a94381826f7c13 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 18 Dec 2020 16:50:25 +0900 Subject: [PATCH 25/60] Fix failing tests due to ignoring the lookup ID --- .../Visual/Online/TestSceneCurrentlyPlayingDisplay.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneCurrentlyPlayingDisplay.cs b/osu.Game.Tests/Visual/Online/TestSceneCurrentlyPlayingDisplay.cs index 4f0ca67e64..eaa881b02c 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneCurrentlyPlayingDisplay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneCurrentlyPlayingDisplay.cs @@ -89,13 +89,11 @@ namespace osu.Game.Tests.Visual.Online "pishifat" }; - private int id; - protected override Task ComputeValueAsync(int lookup, CancellationToken token = default) => Task.FromResult(new User { - Id = id++, - Username = usernames[id % usernames.Length], + Id = lookup++, + Username = usernames[lookup % usernames.Length], }); } } From 2db7433c0b11f5a90be103621af529a2c35f50e7 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 18 Dec 2020 16:51:59 +0900 Subject: [PATCH 26/60] Refactor player score creation and submission process --- .../TestSceneCompletionCancellation.cs | 3 +- .../Screens/Multi/Play/TimeshiftPlayer.cs | 17 ++-- osu.Game/Screens/Play/Player.cs | 79 ++++++++++--------- osu.Game/Screens/Play/ReplayPlayer.cs | 13 +-- 4 files changed, 65 insertions(+), 47 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneCompletionCancellation.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneCompletionCancellation.cs index 6fd5511e5a..6e3b394057 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneCompletionCancellation.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneCompletionCancellation.cs @@ -10,6 +10,7 @@ using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Rulesets; using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Scoring; using osu.Game.Storyboards; using osuTK; @@ -117,7 +118,7 @@ namespace osu.Game.Tests.Visual.Gameplay { } - protected override void GotoRanking() + protected override void GotoRanking(ScoreInfo score) { GotoRankingInvoked = true; } diff --git a/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs b/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs index 0efa9c5196..76e4a328e0 100644 --- a/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs +++ b/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs @@ -5,6 +5,7 @@ using System; using System.Diagnostics; using System.Linq; using System.Threading; +using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Logging; @@ -95,19 +96,25 @@ namespace osu.Game.Screens.Multi.Play return new TimeshiftResultsScreen(score, roomId.Value.Value, playlistItem, true); } - protected override ScoreInfo CreateScore() + protected override Score CreateScore() { var score = base.CreateScore(); - score.TotalScore = (int)Math.Round(ScoreProcessor.GetStandardisedScore()); + score.ScoreInfo.TotalScore = (int)Math.Round(ScoreProcessor.GetStandardisedScore()); + return score; + } + + protected override async Task SubmitScore(Score score) + { + await base.SubmitScore(score); Debug.Assert(token != null); - var request = new SubmitRoomScoreRequest(token.Value, roomId.Value ?? 0, playlistItem.ID, score); - request.Success += s => score.OnlineScoreID = s.ID; + var request = new SubmitRoomScoreRequest(token.Value, roomId.Value ?? 0, playlistItem.ID, score.ScoreInfo); + request.Success += s => score.ScoreInfo.OnlineScoreID = s.ID; request.Failure += e => Logger.Error(e, "Failed to submit score"); api.Queue(request); - return score; + return score.ScoreInfo; } protected override void Dispose(bool isDisposing) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 92c76ec2d2..3fb680b9c9 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Threading.Tasks; using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Audio; @@ -527,8 +528,18 @@ namespace osu.Game.Screens.Play if (!showResults) return; - using (BeginDelayedSequence(RESULTS_DISPLAY_DELAY)) - completionProgressDelegate = Schedule(GotoRanking); + SubmitScore(CreateScore()).ContinueWith(t => Schedule(() => + { + using (BeginDelayedSequence(RESULTS_DISPLAY_DELAY)) + { + completionProgressDelegate = Schedule(() => + { + // screen may be in the exiting transition phase. + if (this.IsCurrentScreen()) + GotoRanking(t.Result); + }); + } + })); } protected override bool OnScroll(ScrollEvent e) => mouseWheelDisabled.Value && !GameplayClockContainer.IsPaused.Value; @@ -727,60 +738,56 @@ namespace osu.Game.Screens.Play return base.OnExiting(next); } - protected virtual ScoreInfo CreateScore() + protected virtual Score CreateScore() { - var score = new ScoreInfo + var score = new Score { - Beatmap = Beatmap.Value.BeatmapInfo, - Ruleset = rulesetInfo, - Mods = Mods.Value.ToArray(), + ScoreInfo = new ScoreInfo + { + Beatmap = Beatmap.Value.BeatmapInfo, + Ruleset = rulesetInfo, + Mods = Mods.Value.ToArray(), + } }; if (DrawableRuleset.ReplayScore != null) - score.User = DrawableRuleset.ReplayScore.ScoreInfo?.User ?? new GuestUser(); + { + score.ScoreInfo.User = DrawableRuleset.ReplayScore.ScoreInfo?.User ?? new GuestUser(); + score.Replay = DrawableRuleset.ReplayScore.Replay; + } else - score.User = api.LocalUser.Value; + { + score.ScoreInfo.User = api.LocalUser.Value; + if (recordingScore?.Replay.Frames.Count > 0) + score.Replay = recordingScore.Replay; + } - ScoreProcessor.PopulateScore(score); + ScoreProcessor.PopulateScore(score.ScoreInfo); return score; } - protected virtual void GotoRanking() + protected virtual async Task SubmitScore(Score score) { + // Replays are already populated and present in the game's database, so should not be re-imported. if (DrawableRuleset.ReplayScore != null) + return score.ScoreInfo; + + LegacyByteArrayReader replayReader; + + using (var stream = new MemoryStream()) { - // if a replay is present, we likely don't want to import into the local database. - this.Push(CreateResults(CreateScore())); - return; + new LegacyScoreEncoder(score, gameplayBeatmap.PlayableBeatmap).Encode(stream); + replayReader = new LegacyByteArrayReader(stream.ToArray(), "replay.osr"); } - LegacyByteArrayReader replayReader = null; - - var score = new Score { ScoreInfo = CreateScore() }; - - if (recordingScore?.Replay.Frames.Count > 0) - { - score.Replay = recordingScore.Replay; - - using (var stream = new MemoryStream()) - { - new LegacyScoreEncoder(score, gameplayBeatmap.PlayableBeatmap).Encode(stream); - replayReader = new LegacyByteArrayReader(stream.ToArray(), "replay.osr"); - } - } - - scoreManager.Import(score.ScoreInfo, replayReader) - .ContinueWith(imported => Schedule(() => - { - // screen may be in the exiting transition phase. - if (this.IsCurrentScreen()) - this.Push(CreateResults(imported.Result)); - })); + return await scoreManager.Import(score.ScoreInfo, replayReader); } protected virtual ResultsScreen CreateResults(ScoreInfo score) => new SoloResultsScreen(score, true); + protected virtual void GotoRanking(ScoreInfo score) => this.Push(CreateResults(score)); + private void fadeOut(bool instant = false) { float fadeOutDuration = instant ? 0 : 250; diff --git a/osu.Game/Screens/Play/ReplayPlayer.cs b/osu.Game/Screens/Play/ReplayPlayer.cs index 294d116f51..390d1d1959 100644 --- a/osu.Game/Screens/Play/ReplayPlayer.cs +++ b/osu.Game/Screens/Play/ReplayPlayer.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.Threading.Tasks; using osu.Framework.Input.Bindings; using osu.Game.Input.Bindings; using osu.Game.Scoring; @@ -26,18 +27,20 @@ namespace osu.Game.Screens.Play DrawableRuleset?.SetReplayScore(Score); } - protected override ResultsScreen CreateResults(ScoreInfo score) => new SoloResultsScreen(score, false); - - protected override ScoreInfo CreateScore() + protected override Score CreateScore() { var baseScore = base.CreateScore(); // Since the replay score doesn't contain statistics, we'll pass them through here. - Score.ScoreInfo.HitEvents = baseScore.HitEvents; + Score.ScoreInfo.HitEvents = baseScore.ScoreInfo.HitEvents; - return Score.ScoreInfo; + return Score; } + protected override Task SubmitScore(Score score) => Task.FromResult(score.ScoreInfo); + + protected override ResultsScreen CreateResults(ScoreInfo score) => new SoloResultsScreen(score, false); + public bool OnPressed(GlobalAction action) { switch (action) From 70cda680c0e868f41f27bd902cd2d0712d7ee4b6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 18 Dec 2020 16:55:55 +0900 Subject: [PATCH 27/60] Update to match new implementation --- osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs index b2dc47ce61..4d39d1a6b3 100644 --- a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs @@ -7,6 +7,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Game.Configuration; using osu.Game.Database; +using osu.Game.Online.API; using osu.Game.Online.Spectator; using osu.Game.Rulesets.Scoring; @@ -44,7 +45,7 @@ namespace osu.Game.Screens.Play.HUD private Bindable scoringMode; [BackgroundDependencyLoader] - private void load(OsuConfigManager config) + private void load(OsuConfigManager config, IAPIProvider api) { streamingClient.OnNewFrames += handleIncomingFrames; @@ -58,7 +59,7 @@ namespace osu.Game.Screens.Play.HUD var trackedUser = new TrackedUserData(); userScores[user] = trackedUser; - AddPlayer(trackedUser.Score, resolvedUser); + Add(new GameplayLeaderboardScore(resolvedUser, resolvedUser.Id == api.LocalUser.Value.Id)); //trackedUser.Score, resolvedUser); } scoringMode = config.GetBindable(OsuSetting.ScoreDisplayMode); From 97ff500b0daa28063188e699411b99017e2241bb Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 18 Dec 2020 16:56:22 +0900 Subject: [PATCH 28/60] Make timeshift wait on score submission --- .../Screens/Multi/Play/TimeshiftPlayer.cs | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs b/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs index 76e4a328e0..e106dc3a1c 100644 --- a/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs +++ b/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs @@ -105,16 +105,29 @@ namespace osu.Game.Screens.Multi.Play protected override async Task SubmitScore(Score score) { - await base.SubmitScore(score); - Debug.Assert(token != null); + bool completed = false; var request = new SubmitRoomScoreRequest(token.Value, roomId.Value ?? 0, playlistItem.ID, score.ScoreInfo); - request.Success += s => score.ScoreInfo.OnlineScoreID = s.ID; - request.Failure += e => Logger.Error(e, "Failed to submit score"); + + request.Success += s => + { + score.ScoreInfo.OnlineScoreID = s.ID; + completed = true; + }; + + request.Failure += e => + { + Logger.Error(e, "Failed to submit score"); + completed = true; + }; + api.Queue(request); - return score.ScoreInfo; + while (!completed) + await Task.Delay(100); + + return await base.SubmitScore(score); } protected override void Dispose(bool isDisposing) From 157a72ec5deb0f5176fd171c57b9e61d30daa958 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 18 Dec 2020 17:07:38 +0900 Subject: [PATCH 29/60] Revert previous player add flow via interface --- .../Gameplay/TestSceneGameplayLeaderboard.cs | 16 +++++++--------- osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs | 16 +++++++++++++--- .../Screens/Play/HUD/GameplayLeaderboardScore.cs | 8 ++++---- osu.Game/Screens/Play/HUD/ILeaderboardScore.cs | 14 ++++++++++++++ 4 files changed, 38 insertions(+), 16 deletions(-) create mode 100644 osu.Game/Screens/Play/HUD/ILeaderboardScore.cs diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs index d596def98a..6e9375f69c 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs @@ -39,7 +39,7 @@ namespace osu.Game.Tests.Visual.Gameplay playerScore.Value = 1222333; }); - AddStep("add local player", () => leaderboard.Add(createLeaderboardScore(playerScore, "You", true))); + AddStep("add local player", () => createLeaderboardScore(playerScore, "You", true)); AddSliderStep("set player score", 50, 5000000, 1222333, v => playerScore.Value = v); } @@ -49,8 +49,8 @@ namespace osu.Game.Tests.Visual.Gameplay var player2Score = new BindableDouble(1234567); var player3Score = new BindableDouble(1111111); - AddStep("add player 2", () => leaderboard.Add(createLeaderboardScore(player2Score, "Player 2"))); - AddStep("add player 3", () => leaderboard.Add(createLeaderboardScore(player3Score, "Player 3"))); + AddStep("add player 2", () => createLeaderboardScore(player2Score, "Player 2")); + AddStep("add player 3", () => createLeaderboardScore(player3Score, "Player 3")); AddAssert("is player 2 position #1", () => leaderboard.CheckPositionByUsername("Player 2", 1)); AddAssert("is player position #2", () => leaderboard.CheckPositionByUsername("You", 2)); @@ -71,15 +71,13 @@ namespace osu.Game.Tests.Visual.Gameplay public void TestRandomScores() { int playerNumber = 1; - AddRepeatStep("add player with random score", () => leaderboard.Add(createLeaderboardScore(new BindableDouble(RNG.Next(0, 5_000_000)), $"Player {playerNumber++}")), 10); + AddRepeatStep("add player with random score", () => createLeaderboardScore(new BindableDouble(RNG.Next(0, 5_000_000)), $"Player {playerNumber++}"), 10); } - private static GameplayLeaderboardScore createLeaderboardScore(BindableDouble score, string username, bool localOrReplayPlayer = false) + private void createLeaderboardScore(BindableDouble score, string username, bool isTracked = false) { - return new GameplayLeaderboardScore(new User { Username = username }, localOrReplayPlayer) - { - TotalScore = { BindTarget = score }, - }; + var leaderboardScore = leaderboard.AddPlayer(new User { Username = username }, isTracked); + leaderboardScore.TotalScore.BindTo(score); } private class TestGameplayLeaderboard : GameplayLeaderboard diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs index 573bf54b14..e738477a80 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboard.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.Linq; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Game.Users; using osuTK; namespace osu.Game.Screens.Play.HUD @@ -22,13 +24,21 @@ namespace osu.Game.Screens.Play.HUD LayoutEasing = Easing.OutQuint; } - public override void Add(GameplayLeaderboardScore drawable) + public ILeaderboardScore AddPlayer(User user, bool isTracked) { + var drawable = new GameplayLeaderboardScore(user, isTracked); base.Add(drawable); - drawable?.TotalScore.BindValueChanged(_ => updateScores(), true); + drawable.TotalScore.BindValueChanged(_ => Scheduler.AddOnce(sort), true); + + return drawable; } - private void updateScores() + public override void Add(GameplayLeaderboardScore drawable) + { + throw new InvalidOperationException($"Use {nameof(AddPlayer)} instead."); + } + + private void sort() { var orderedByScore = this.OrderByDescending(i => i.TotalScore.Value).ToList(); diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs index 439210c944..d4d7c69f6b 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs @@ -17,7 +17,7 @@ using osuTK.Graphics; namespace osu.Game.Screens.Play.HUD { - public class GameplayLeaderboardScore : CompositeDrawable + public class GameplayLeaderboardScore : CompositeDrawable, ILeaderboardScore { private const float regular_width = 215f; private const float extended_width = 235f; @@ -26,9 +26,9 @@ namespace osu.Game.Screens.Play.HUD private OsuSpriteText positionText, scoreText, accuracyText, comboText, usernameText; - public readonly BindableDouble TotalScore = new BindableDouble(1000000); - public readonly BindableDouble Accuracy = new BindableDouble(1); - public readonly BindableInt Combo = new BindableInt(); + public BindableDouble TotalScore { get; } = new BindableDouble(); + public BindableDouble Accuracy { get; } = new BindableDouble(1); + public BindableInt Combo { get; } = new BindableInt(); private int? scorePosition; diff --git a/osu.Game/Screens/Play/HUD/ILeaderboardScore.cs b/osu.Game/Screens/Play/HUD/ILeaderboardScore.cs new file mode 100644 index 0000000000..bc1a03c5aa --- /dev/null +++ b/osu.Game/Screens/Play/HUD/ILeaderboardScore.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 osu.Framework.Bindables; + +namespace osu.Game.Screens.Play.HUD +{ + public interface ILeaderboardScore + { + BindableDouble TotalScore { get; } + BindableDouble Accuracy { get; } + BindableInt Combo { get; } + } +} From cb3f89d0a5c73baa360413c9ef9cf08c8ea39617 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 18 Dec 2020 17:13:51 +0900 Subject: [PATCH 30/60] Hook up with new leaderboard design --- osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs index 4d39d1a6b3..c96f496cd0 100644 --- a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs @@ -59,7 +59,11 @@ namespace osu.Game.Screens.Play.HUD var trackedUser = new TrackedUserData(); userScores[user] = trackedUser; - Add(new GameplayLeaderboardScore(resolvedUser, resolvedUser.Id == api.LocalUser.Value.Id)); //trackedUser.Score, resolvedUser); + var leaderboardScore = AddPlayer(resolvedUser, resolvedUser.Id == api.LocalUser.Value.Id); + + ((IBindable)leaderboardScore.Accuracy).BindTo(trackedUser.Accuracy); + ((IBindable)leaderboardScore.TotalScore).BindTo(trackedUser.Score); + ((IBindable)leaderboardScore.Combo).BindTo(trackedUser.CurrentCombo); } scoringMode = config.GetBindable(OsuSetting.ScoreDisplayMode); From bca4d83af794a587eb02eba41357ce6e841381c1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 18 Dec 2020 17:07:38 +0900 Subject: [PATCH 31/60] Revert previous player add flow via interface --- .../Gameplay/TestSceneGameplayLeaderboard.cs | 16 +++++++--------- osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs | 16 +++++++++++++--- .../Screens/Play/HUD/GameplayLeaderboardScore.cs | 8 ++++---- osu.Game/Screens/Play/HUD/ILeaderboardScore.cs | 14 ++++++++++++++ 4 files changed, 38 insertions(+), 16 deletions(-) create mode 100644 osu.Game/Screens/Play/HUD/ILeaderboardScore.cs diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs index d596def98a..6e9375f69c 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs @@ -39,7 +39,7 @@ namespace osu.Game.Tests.Visual.Gameplay playerScore.Value = 1222333; }); - AddStep("add local player", () => leaderboard.Add(createLeaderboardScore(playerScore, "You", true))); + AddStep("add local player", () => createLeaderboardScore(playerScore, "You", true)); AddSliderStep("set player score", 50, 5000000, 1222333, v => playerScore.Value = v); } @@ -49,8 +49,8 @@ namespace osu.Game.Tests.Visual.Gameplay var player2Score = new BindableDouble(1234567); var player3Score = new BindableDouble(1111111); - AddStep("add player 2", () => leaderboard.Add(createLeaderboardScore(player2Score, "Player 2"))); - AddStep("add player 3", () => leaderboard.Add(createLeaderboardScore(player3Score, "Player 3"))); + AddStep("add player 2", () => createLeaderboardScore(player2Score, "Player 2")); + AddStep("add player 3", () => createLeaderboardScore(player3Score, "Player 3")); AddAssert("is player 2 position #1", () => leaderboard.CheckPositionByUsername("Player 2", 1)); AddAssert("is player position #2", () => leaderboard.CheckPositionByUsername("You", 2)); @@ -71,15 +71,13 @@ namespace osu.Game.Tests.Visual.Gameplay public void TestRandomScores() { int playerNumber = 1; - AddRepeatStep("add player with random score", () => leaderboard.Add(createLeaderboardScore(new BindableDouble(RNG.Next(0, 5_000_000)), $"Player {playerNumber++}")), 10); + AddRepeatStep("add player with random score", () => createLeaderboardScore(new BindableDouble(RNG.Next(0, 5_000_000)), $"Player {playerNumber++}"), 10); } - private static GameplayLeaderboardScore createLeaderboardScore(BindableDouble score, string username, bool localOrReplayPlayer = false) + private void createLeaderboardScore(BindableDouble score, string username, bool isTracked = false) { - return new GameplayLeaderboardScore(new User { Username = username }, localOrReplayPlayer) - { - TotalScore = { BindTarget = score }, - }; + var leaderboardScore = leaderboard.AddPlayer(new User { Username = username }, isTracked); + leaderboardScore.TotalScore.BindTo(score); } private class TestGameplayLeaderboard : GameplayLeaderboard diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs index 573bf54b14..e738477a80 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboard.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.Linq; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Game.Users; using osuTK; namespace osu.Game.Screens.Play.HUD @@ -22,13 +24,21 @@ namespace osu.Game.Screens.Play.HUD LayoutEasing = Easing.OutQuint; } - public override void Add(GameplayLeaderboardScore drawable) + public ILeaderboardScore AddPlayer(User user, bool isTracked) { + var drawable = new GameplayLeaderboardScore(user, isTracked); base.Add(drawable); - drawable?.TotalScore.BindValueChanged(_ => updateScores(), true); + drawable.TotalScore.BindValueChanged(_ => Scheduler.AddOnce(sort), true); + + return drawable; } - private void updateScores() + public override void Add(GameplayLeaderboardScore drawable) + { + throw new InvalidOperationException($"Use {nameof(AddPlayer)} instead."); + } + + private void sort() { var orderedByScore = this.OrderByDescending(i => i.TotalScore.Value).ToList(); diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs index 439210c944..d4d7c69f6b 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs @@ -17,7 +17,7 @@ using osuTK.Graphics; namespace osu.Game.Screens.Play.HUD { - public class GameplayLeaderboardScore : CompositeDrawable + public class GameplayLeaderboardScore : CompositeDrawable, ILeaderboardScore { private const float regular_width = 215f; private const float extended_width = 235f; @@ -26,9 +26,9 @@ namespace osu.Game.Screens.Play.HUD private OsuSpriteText positionText, scoreText, accuracyText, comboText, usernameText; - public readonly BindableDouble TotalScore = new BindableDouble(1000000); - public readonly BindableDouble Accuracy = new BindableDouble(1); - public readonly BindableInt Combo = new BindableInt(); + public BindableDouble TotalScore { get; } = new BindableDouble(); + public BindableDouble Accuracy { get; } = new BindableDouble(1); + public BindableInt Combo { get; } = new BindableInt(); private int? scorePosition; diff --git a/osu.Game/Screens/Play/HUD/ILeaderboardScore.cs b/osu.Game/Screens/Play/HUD/ILeaderboardScore.cs new file mode 100644 index 0000000000..bc1a03c5aa --- /dev/null +++ b/osu.Game/Screens/Play/HUD/ILeaderboardScore.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 osu.Framework.Bindables; + +namespace osu.Game.Screens.Play.HUD +{ + public interface ILeaderboardScore + { + BindableDouble TotalScore { get; } + BindableDouble Accuracy { get; } + BindableInt Combo { get; } + } +} From 4cf013c0058555776eea78a956203cfbbfe8d70a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 18 Dec 2020 17:19:00 +0900 Subject: [PATCH 32/60] Fix animation replacing itself even when score position hasn't changed --- osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs index d4d7c69f6b..086ac956e8 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs @@ -37,6 +37,9 @@ namespace osu.Game.Screens.Play.HUD get => scorePosition; set { + if (value == scorePosition) + return; + scorePosition = value; if (scorePosition.HasValue) From e82986b76328983a9c648ba274df7689115941af Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 18 Dec 2020 17:19:36 +0900 Subject: [PATCH 33/60] Fix panel x positions getting weird duration relayouts Also adjust the transitions a bit to feel better. --- .../Play/HUD/GameplayLeaderboardScore.cs | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs index 086ac956e8..77e6d320ae 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs @@ -67,11 +67,7 @@ namespace osu.Game.Screens.Play.HUD User = user; this.trackedPlayer = trackedPlayer; - AutoSizeAxes = Axes.X; - Height = panel_height; - - Anchor = Anchor.TopRight; - Origin = Anchor.TopRight; + Size = new Vector2(extended_width, panel_height); } protected override void LoadComplete() @@ -82,23 +78,25 @@ namespace osu.Game.Screens.Play.HUD FinishTransforms(true); } + private const double transition_duration = 500; + private void updateColour() { if (scorePosition == 1) { - mainFillContainer.ResizeWidthTo(extended_width, 200, Easing.OutQuint); + mainFillContainer.ResizeWidthTo(extended_width, transition_duration, Easing.OutElastic); panelColour = Color4Extensions.FromHex("7fcc33"); textColour = Color4.White; } else if (trackedPlayer) { - mainFillContainer.ResizeWidthTo(extended_width, 200, Easing.OutQuint); + mainFillContainer.ResizeWidthTo(extended_width, transition_duration, Easing.OutElastic); panelColour = Color4Extensions.FromHex("ffd966"); textColour = Color4Extensions.FromHex("2e576b"); } else { - mainFillContainer.ResizeWidthTo(regular_width, 200, Easing.OutQuint); + mainFillContainer.ResizeWidthTo(regular_width, transition_duration, Easing.OutElastic); panelColour = Color4Extensions.FromHex("3399cc"); textColour = Color4.White; } @@ -108,8 +106,8 @@ namespace osu.Game.Screens.Play.HUD { set { - mainFillContainer.FadeColour(value, 200, Easing.OutQuint); - centralFill.FadeColour(value, 200, Easing.OutQuint); + mainFillContainer.FadeColour(value, transition_duration, Easing.OutQuint); + centralFill.FadeColour(value, transition_duration, Easing.OutQuint); } } From 668536ce562eb30088b66602f294672b5be58d58 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 18 Dec 2020 17:25:48 +0900 Subject: [PATCH 34/60] Fix vertical size potentially changing during relayout --- .../Gameplay/TestSceneGameplayLeaderboard.cs | 2 +- osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs | 4 +++- .../Screens/Play/HUD/GameplayLeaderboardScore.cs | 15 ++++++++------- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs index 6e9375f69c..ff15e1d2dc 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs @@ -25,7 +25,7 @@ namespace osu.Game.Tests.Visual.Gameplay Add(leaderboard = new TestGameplayLeaderboard { Anchor = Anchor.Centre, - Origin = Anchor.TopCentre, + Origin = Anchor.Centre, Scale = new Vector2(2), }); } diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs index e738477a80..99319c0008 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs @@ -14,7 +14,7 @@ namespace osu.Game.Screens.Play.HUD { public GameplayLeaderboard() { - AutoSizeAxes = Axes.Both; + Width = GameplayLeaderboardScore.EXTENDED_WIDTH; Direction = FillDirection.Vertical; @@ -30,6 +30,8 @@ namespace osu.Game.Screens.Play.HUD base.Add(drawable); drawable.TotalScore.BindValueChanged(_ => Scheduler.AddOnce(sort), true); + Height = Count * (GameplayLeaderboardScore.PANEL_HEIGHT + Spacing.Y); + return drawable; } diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs index 77e6d320ae..242e7ff6b9 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs @@ -19,10 +19,11 @@ namespace osu.Game.Screens.Play.HUD { public class GameplayLeaderboardScore : CompositeDrawable, ILeaderboardScore { - private const float regular_width = 215f; - private const float extended_width = 235f; + public const float EXTENDED_WIDTH = 235f; - private const float panel_height = 35f; + private const float regular_width = 215f; + + public const float PANEL_HEIGHT = 35f; private OsuSpriteText positionText, scoreText, accuracyText, comboText, usernameText; @@ -67,7 +68,7 @@ namespace osu.Game.Screens.Play.HUD User = user; this.trackedPlayer = trackedPlayer; - Size = new Vector2(extended_width, panel_height); + Size = new Vector2(EXTENDED_WIDTH, PANEL_HEIGHT); } protected override void LoadComplete() @@ -84,13 +85,13 @@ namespace osu.Game.Screens.Play.HUD { if (scorePosition == 1) { - mainFillContainer.ResizeWidthTo(extended_width, transition_duration, Easing.OutElastic); + mainFillContainer.ResizeWidthTo(EXTENDED_WIDTH, transition_duration, Easing.OutElastic); panelColour = Color4Extensions.FromHex("7fcc33"); textColour = Color4.White; } else if (trackedPlayer) { - mainFillContainer.ResizeWidthTo(extended_width, transition_duration, Easing.OutElastic); + mainFillContainer.ResizeWidthTo(EXTENDED_WIDTH, transition_duration, Easing.OutElastic); panelColour = Color4Extensions.FromHex("ffd966"); textColour = Color4Extensions.FromHex("2e576b"); } @@ -127,7 +128,7 @@ namespace osu.Game.Screens.Play.HUD private void load() { const float panel_shear = 0.15f; - const float shear_width = panel_height * panel_shear; + const float shear_width = PANEL_HEIGHT * panel_shear; InternalChildren = new Drawable[] { From 615352c1e41a13c9dc189003aeb4ee2b8a3f95df Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 18 Dec 2020 17:30:11 +0900 Subject: [PATCH 35/60] Fix shear offset not being included in GameplayLeaderboard's own size --- osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs | 9 +++++++-- .../Screens/Play/HUD/GameplayLeaderboardScore.cs | 14 ++++++++------ 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs index 99319c0008..aa668cc4c8 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs @@ -14,7 +14,7 @@ namespace osu.Game.Screens.Play.HUD { public GameplayLeaderboard() { - Width = GameplayLeaderboardScore.EXTENDED_WIDTH; + Width = GameplayLeaderboardScore.EXTENDED_WIDTH + GameplayLeaderboardScore.SHEAR_WIDTH; Direction = FillDirection.Vertical; @@ -26,7 +26,12 @@ namespace osu.Game.Screens.Play.HUD public ILeaderboardScore AddPlayer(User user, bool isTracked) { - var drawable = new GameplayLeaderboardScore(user, isTracked); + var drawable = new GameplayLeaderboardScore(user, isTracked) + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + }; + base.Add(drawable); drawable.TotalScore.BindValueChanged(_ => Scheduler.AddOnce(sort), true); diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs index 242e7ff6b9..5c4b4406cc 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs @@ -16,6 +16,7 @@ using osuTK; using osuTK.Graphics; namespace osu.Game.Screens.Play.HUD + { public class GameplayLeaderboardScore : CompositeDrawable, ILeaderboardScore { @@ -25,6 +26,10 @@ namespace osu.Game.Screens.Play.HUD public const float PANEL_HEIGHT = 35f; + public const float SHEAR_WIDTH = PANEL_HEIGHT * panel_shear; + + private const float panel_shear = 0.15f; + private OsuSpriteText positionText, scoreText, accuracyText, comboText, usernameText; public BindableDouble TotalScore { get; } = new BindableDouble(); @@ -127,9 +132,6 @@ namespace osu.Game.Screens.Play.HUD [BackgroundDependencyLoader] private void load() { - const float panel_shear = 0.15f; - const float shear_width = PANEL_HEIGHT * panel_shear; - InternalChildren = new Drawable[] { mainFillContainer = new Container @@ -165,7 +167,7 @@ namespace osu.Game.Screens.Play.HUD { positionText = new OsuSpriteText { - Padding = new MarginPadding { Right = shear_width / 2 }, + Padding = new MarginPadding { Right = SHEAR_WIDTH / 2 }, Anchor = Anchor.Centre, Origin = Anchor.Centre, Colour = Color4.White, @@ -174,7 +176,7 @@ namespace osu.Game.Screens.Play.HUD }, new Container { - Padding = new MarginPadding { Horizontal = shear_width / 3 }, + Padding = new MarginPadding { Horizontal = SHEAR_WIDTH / 3 }, RelativeSizeAxes = Axes.Both, Children = new Drawable[] { @@ -196,7 +198,7 @@ namespace osu.Game.Screens.Play.HUD }, usernameText = new OsuSpriteText { - Padding = new MarginPadding { Left = shear_width }, + Padding = new MarginPadding { Left = SHEAR_WIDTH }, RelativeSizeAxes = Axes.X, Width = 0.8f, Anchor = Anchor.CentreLeft, From fdad5e86d3baa4adb991717e8408afae2da02bb0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 18 Dec 2020 17:33:18 +0900 Subject: [PATCH 36/60] Remove stray newline --- osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs index 5c4b4406cc..0b58cf76a2 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs @@ -16,7 +16,6 @@ using osuTK; using osuTK.Graphics; namespace osu.Game.Screens.Play.HUD - { public class GameplayLeaderboardScore : CompositeDrawable, ILeaderboardScore { From 2958cab239027632f0b9b5c61109bbbd90bfa8b4 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 18 Dec 2020 17:47:33 +0900 Subject: [PATCH 37/60] Remove GotoRanking --- .../TestSceneCompletionCancellation.cs | 13 ++++++++----- osu.Game/Screens/Play/Player.cs | 18 +++++++++++++++--- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneCompletionCancellation.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneCompletionCancellation.cs index 6e3b394057..4ee48fd853 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneCompletionCancellation.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneCompletionCancellation.cs @@ -11,6 +11,7 @@ using osu.Game.Beatmaps; using osu.Game.Rulesets; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Scoring; +using osu.Game.Screens.Ranking; using osu.Game.Storyboards; using osuTK; @@ -51,7 +52,7 @@ namespace osu.Game.Tests.Visual.Gameplay cancel(); complete(); - AddUntilStep("attempted to push ranking", () => ((FakeRankingPushPlayer)Player).GotoRankingInvoked); + AddUntilStep("attempted to push ranking", () => ((FakeRankingPushPlayer)Player).ResultsCreated); } /// @@ -85,7 +86,7 @@ namespace osu.Game.Tests.Visual.Gameplay { // wait to ensure there was no attempt of pushing the results screen. AddWaitStep("wait", resultsDisplayWaitCount); - AddAssert("no attempt to push ranking", () => !((FakeRankingPushPlayer)Player).GotoRankingInvoked); + AddAssert("no attempt to push ranking", () => !((FakeRankingPushPlayer)Player).ResultsCreated); } protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard = null) @@ -111,16 +112,18 @@ namespace osu.Game.Tests.Visual.Gameplay public class FakeRankingPushPlayer : TestPlayer { - public bool GotoRankingInvoked; + public bool ResultsCreated { get; private set; } public FakeRankingPushPlayer() : base(true, true) { } - protected override void GotoRanking(ScoreInfo score) + protected override ResultsScreen CreateResults(ScoreInfo score) { - GotoRankingInvoked = true; + var results = base.CreateResults(score); + ResultsCreated = true; + return results; } } } diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 3fb680b9c9..cc5f32d300 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -536,7 +536,7 @@ namespace osu.Game.Screens.Play { // screen may be in the exiting transition phase. if (this.IsCurrentScreen()) - GotoRanking(t.Result); + this.Push(CreateResults(t.Result)); }); } })); @@ -738,6 +738,10 @@ namespace osu.Game.Screens.Play return base.OnExiting(next); } + /// + /// Creates the player's . + /// + /// The . protected virtual Score CreateScore() { var score = new Score @@ -767,6 +771,11 @@ namespace osu.Game.Screens.Play return score; } + /// + /// Submits the player's . + /// + /// The to submit. + /// The submitted score. protected virtual async Task SubmitScore(Score score) { // Replays are already populated and present in the game's database, so should not be re-imported. @@ -784,10 +793,13 @@ namespace osu.Game.Screens.Play return await scoreManager.Import(score.ScoreInfo, replayReader); } + /// + /// Creates the for a . + /// + /// The to be displayed in the results screen. + /// The . protected virtual ResultsScreen CreateResults(ScoreInfo score) => new SoloResultsScreen(score, true); - protected virtual void GotoRanking(ScoreInfo score) => this.Push(CreateResults(score)); - private void fadeOut(bool instant = false) { float fadeOutDuration = instant ? 0 : 250; From 1369b75a86b93cac21c22c0d4a9305b9864f9258 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 18 Dec 2020 17:48:42 +0900 Subject: [PATCH 38/60] Fix potential multiple submission --- osu.Game/Screens/Play/Player.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index cc5f32d300..94c595908e 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -502,6 +502,7 @@ namespace osu.Game.Screens.Play } private ScheduledDelegate completionProgressDelegate; + private Task scoreSubmissionTask; private void updateCompletionState(ValueChangedEvent completionState) { @@ -528,7 +529,8 @@ namespace osu.Game.Screens.Play if (!showResults) return; - SubmitScore(CreateScore()).ContinueWith(t => Schedule(() => + scoreSubmissionTask ??= SubmitScore(CreateScore()); + scoreSubmissionTask.ContinueWith(t => Schedule(() => { using (BeginDelayedSequence(RESULTS_DISPLAY_DELAY)) { From 8826d0155908750dfcc99df83bed41e548bb3391 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 18 Dec 2020 18:20:36 +0900 Subject: [PATCH 39/60] Create completion progress delegate immediately --- osu.Game/Screens/Play/Player.cs | 37 ++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 94c595908e..b79b8eeae8 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -529,21 +529,38 @@ namespace osu.Game.Screens.Play if (!showResults) return; - scoreSubmissionTask ??= SubmitScore(CreateScore()); - scoreSubmissionTask.ContinueWith(t => Schedule(() => + scoreSubmissionTask ??= Task.Run(async () => { - using (BeginDelayedSequence(RESULTS_DISPLAY_DELAY)) + var score = CreateScore(); + + try { - completionProgressDelegate = Schedule(() => - { - // screen may be in the exiting transition phase. - if (this.IsCurrentScreen()) - this.Push(CreateResults(t.Result)); - }); + return await SubmitScore(score); } - })); + catch (Exception ex) + { + Logger.Error(ex, "Score submission failed!"); + return score.ScoreInfo; + } + }); + + using (BeginDelayedSequence(RESULTS_DISPLAY_DELAY)) + scheduleCompletion(); } + private void scheduleCompletion() => completionProgressDelegate = Schedule(() => + { + if (!scoreSubmissionTask.IsCompleted) + { + scheduleCompletion(); + return; + } + + // screen may be in the exiting transition phase. + if (this.IsCurrentScreen()) + this.Push(CreateResults(scoreSubmissionTask.Result)); + }); + protected override bool OnScroll(ScrollEvent e) => mouseWheelDisabled.Value && !GameplayClockContainer.IsPaused.Value; #region Fail Logic From eccfc8ccd2b420c99cc2b30f868ad74e1214881a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 18 Dec 2020 18:31:49 +0900 Subject: [PATCH 40/60] Fix potential cross-reference access --- 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 b79b8eeae8..c8f1980ab1 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -23,8 +23,10 @@ using osu.Game.Graphics.Containers; using osu.Game.IO.Archives; using osu.Game.Online.API; using osu.Game.Overlays; +using osu.Game.Replays; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; using osu.Game.Scoring; @@ -781,8 +783,7 @@ namespace osu.Game.Screens.Play else { score.ScoreInfo.User = api.LocalUser.Value; - if (recordingScore?.Replay.Frames.Count > 0) - score.Replay = recordingScore.Replay; + score.Replay = new Replay { Frames = recordingScore?.Replay.Frames.ToList() ?? new List() }; } ScoreProcessor.PopulateScore(score.ScoreInfo); From c9e75e790830816cd4b90525a71a625319e43bae Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 18 Dec 2020 13:09:05 +0300 Subject: [PATCH 41/60] Add user avatar to leaderboard scores --- .../Play/HUD/GameplayLeaderboardScore.cs | 53 +++++++++++++++---- 1 file changed, 43 insertions(+), 10 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs index 0b58cf76a2..9c444a6f1f 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs @@ -11,6 +11,7 @@ using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Users; +using osu.Game.Users.Drawables; using osu.Game.Utils; using osuTK; using osuTK.Graphics; @@ -129,7 +130,7 @@ namespace osu.Game.Screens.Play.HUD } [BackgroundDependencyLoader] - private void load() + private void load(OsuColour colours) { InternalChildren = new Drawable[] { @@ -195,19 +196,51 @@ namespace osu.Game.Screens.Play.HUD }, } }, - usernameText = new OsuSpriteText + new FillFlowContainer { Padding = new MarginPadding { Left = SHEAR_WIDTH }, - RelativeSizeAxes = Axes.X, - Width = 0.8f, Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, - Colour = Color4.White, - Font = OsuFont.Torus.With(size: 14, weight: FontWeight.SemiBold), - Text = User.Username, - Truncate = true, - Shadow = false, - } + RelativeSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(4f, 0f), + Children = new Drawable[] + { + new CircularContainer + { + Masking = true, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Size = new Vector2(25f), + Children = new Drawable[] + { + new Box + { + Name = "Placeholder while avatar loads", + Alpha = 0.3f, + RelativeSizeAxes = Axes.Both, + Colour = colours.Gray4, + }, + new UpdateableAvatar(User) + { + RelativeSizeAxes = Axes.Both, + }, + } + }, + usernameText = new OsuSpriteText + { + RelativeSizeAxes = Axes.X, + Width = 0.8f, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Colour = Color4.White, + Font = OsuFont.Torus.With(size: 14, weight: FontWeight.SemiBold), + Text = User.Username, + Truncate = true, + Shadow = false, + } + } + }, } }, new Container From 030dce55599422cc4017b5b754eab9a66cee623a Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 18 Dec 2020 13:09:50 +0300 Subject: [PATCH 42/60] Increase leaderboard score width a bit --- osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs index 9c444a6f1f..af3cb640bb 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs @@ -20,9 +20,9 @@ namespace osu.Game.Screens.Play.HUD { public class GameplayLeaderboardScore : CompositeDrawable, ILeaderboardScore { - public const float EXTENDED_WIDTH = 235f; + public const float EXTENDED_WIDTH = 255f; - private const float regular_width = 215f; + private const float regular_width = 235f; public const float PANEL_HEIGHT = 35f; From 228acf25a7d1b483841f4c6b1bbc4ab46c15aad6 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 18 Dec 2020 13:13:31 +0300 Subject: [PATCH 43/60] Add test case creating leaderboard scores with existing users --- .../Gameplay/TestSceneGameplayLeaderboard.cs | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs index ff15e1d2dc..ca61672ef9 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs @@ -39,7 +39,7 @@ namespace osu.Game.Tests.Visual.Gameplay playerScore.Value = 1222333; }); - AddStep("add local player", () => createLeaderboardScore(playerScore, "You", true)); + AddStep("add local player", () => createLeaderboardScore(playerScore, new User { Username = "You", Id = 3 }, true)); AddSliderStep("set player score", 50, 5000000, 1222333, v => playerScore.Value = v); } @@ -49,8 +49,8 @@ namespace osu.Game.Tests.Visual.Gameplay var player2Score = new BindableDouble(1234567); var player3Score = new BindableDouble(1111111); - AddStep("add player 2", () => createLeaderboardScore(player2Score, "Player 2")); - AddStep("add player 3", () => createLeaderboardScore(player3Score, "Player 3")); + AddStep("add player 2", () => createLeaderboardScore(player2Score, new User { Username = "Player 2" })); + AddStep("add player 3", () => createLeaderboardScore(player3Score, new User { Username = "Player 3" })); AddAssert("is player 2 position #1", () => leaderboard.CheckPositionByUsername("Player 2", 1)); AddAssert("is player position #2", () => leaderboard.CheckPositionByUsername("You", 2)); @@ -71,12 +71,23 @@ namespace osu.Game.Tests.Visual.Gameplay public void TestRandomScores() { int playerNumber = 1; - AddRepeatStep("add player with random score", () => createLeaderboardScore(new BindableDouble(RNG.Next(0, 5_000_000)), $"Player {playerNumber++}"), 10); + AddRepeatStep("add player with random score", () => createRandomScore(new User { Username = $"Player {playerNumber++}" }), 10); } - private void createLeaderboardScore(BindableDouble score, string username, bool isTracked = false) + [Test] + public void TestExistingUsers() { - var leaderboardScore = leaderboard.AddPlayer(new User { Username = username }, isTracked); + AddStep("add peppy", () => createRandomScore(new User { Username = "peppy", Id = 2 })); + AddStep("add smoogipoo", () => createRandomScore(new User { Username = "smoogipoo", Id = 1040328 })); + AddStep("add flyte", () => createRandomScore(new User { Username = "flyte", Id = 3103765 })); + AddStep("add frenzibyte", () => createRandomScore(new User { Username = "frenzibyte", Id = 14210502 })); + } + + 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) + { + var leaderboardScore = leaderboard.AddPlayer(user, isTracked); leaderboardScore.TotalScore.BindTo(score); } From 122250f454059d9adc231e982bd976e3795e1beb Mon Sep 17 00:00:00 2001 From: Graham Johnson Date: Fri, 18 Dec 2020 10:45:23 -0500 Subject: [PATCH 44/60] replace drag cirle function with dictionary --- .../Edit/Compose/Components/SelectionBox.cs | 61 ++++++------------- 1 file changed, 17 insertions(+), 44 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs index 8ccd4f71e9..85e86499be 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.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; @@ -235,13 +236,27 @@ namespace osu.Game.Screens.Edit.Compose.Components private void addDragHandle(Anchor anchor) => AddInternal(new SelectionBoxDragHandle { Anchor = anchor, - X = getAdjustmentToCenterCircleOnBorder(anchor).X, - Y = getAdjustmentToCenterCircleOnBorder(anchor).Y, + X = dragCircleAdjustments[anchor].X, + Y = dragCircleAdjustments[anchor].Y, HandleDrag = e => OnScale?.Invoke(e.Delta, anchor), OperationStarted = operationStarted, OperationEnded = operationEnded }); + /// + /// Adjust Drag circle to be centered on the center of the border instead of on the edge. + /// + private Dictionary dragCircleAdjustments = new Dictionary(){ + {Anchor.TopLeft, new Vector2(BORDER_RADIUS / 2)}, + {Anchor.CentreLeft, new Vector2(BORDER_RADIUS / 2, 0)}, + {Anchor.BottomLeft, new Vector2(BORDER_RADIUS / 2, -BORDER_RADIUS / 2)}, + {Anchor.TopCentre, new Vector2(0, BORDER_RADIUS / 2)}, + {Anchor.BottomCentre, new Vector2(0, -BORDER_RADIUS / 2)}, + {Anchor.TopRight, new Vector2(-BORDER_RADIUS / 2, BORDER_RADIUS / 2)}, + {Anchor.CentreRight, new Vector2(-BORDER_RADIUS / 2, 0)}, + {Anchor.BottomRight, new Vector2(-BORDER_RADIUS / 2)} + }; + private int activeOperations; private float convertDragEventToAngleOfRotation(DragEvent e) @@ -253,48 +268,6 @@ namespace osu.Game.Screens.Edit.Compose.Components return (endAngle - startAngle) * 180 / MathF.PI; } - /// - /// Adjust Drag circle to be centered on the center of the border instead of on the edge. - /// - /// The part of the rectangle to be adjusted. - /// A 2d vector on how much to adjust the drag circle - private Vector2 getAdjustmentToCenterCircleOnBorder(Anchor anchor) - { - Vector2 adjustment = Vector2.Zero; - - switch (anchor) - { - case Anchor.TopLeft: - case Anchor.CentreLeft: - case Anchor.BottomLeft: - adjustment.X = BORDER_RADIUS / 2; - break; - - case Anchor.TopRight: - case Anchor.CentreRight: - case Anchor.BottomRight: - adjustment.X = -BORDER_RADIUS / 2; - break; - } - - switch (anchor) - { - case Anchor.TopLeft: - case Anchor.TopCentre: - case Anchor.TopRight: - adjustment.Y = BORDER_RADIUS / 2; - break; - - case Anchor.BottomLeft: - case Anchor.BottomCentre: - case Anchor.BottomRight: - adjustment.Y = -BORDER_RADIUS / 2; - break; - } - - return adjustment; - } - private void operationEnded() { if (--activeOperations == 0) From cc22efaa6babc4592a06a34f6affb226caea56cb Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 19 Dec 2020 03:17:04 +0900 Subject: [PATCH 45/60] Use tcs instead of delay-wait --- osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs b/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs index e106dc3a1c..2b7ca189ee 100644 --- a/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs +++ b/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs @@ -107,25 +107,23 @@ namespace osu.Game.Screens.Multi.Play { Debug.Assert(token != null); - bool completed = false; + var tcs = new TaskCompletionSource(); var request = new SubmitRoomScoreRequest(token.Value, roomId.Value ?? 0, playlistItem.ID, score.ScoreInfo); request.Success += s => { score.ScoreInfo.OnlineScoreID = s.ID; - completed = true; + tcs.SetResult(true); }; request.Failure += e => { Logger.Error(e, "Failed to submit score"); - completed = true; + tcs.SetResult(false); }; api.Queue(request); - - while (!completed) - await Task.Delay(100); + await tcs.Task; return await base.SubmitScore(score); } From 772dd0287e4932e232d4f6c033b7246f740d3541 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 19 Dec 2020 03:32:05 +0900 Subject: [PATCH 46/60] Split submission and import into two methods --- .../Screens/Multi/Play/TimeshiftPlayer.cs | 6 ++-- osu.Game/Screens/Play/Player.cs | 33 ++++++++++++++----- osu.Game/Screens/Play/ReplayPlayer.cs | 3 +- 3 files changed, 30 insertions(+), 12 deletions(-) diff --git a/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs b/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs index 2b7ca189ee..41dcf61740 100644 --- a/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs +++ b/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs @@ -103,8 +103,10 @@ namespace osu.Game.Screens.Multi.Play return score; } - protected override async Task SubmitScore(Score score) + protected override async Task SubmitScore(Score score) { + await base.SubmitScore(score); + Debug.Assert(token != null); var tcs = new TaskCompletionSource(); @@ -124,8 +126,6 @@ namespace osu.Game.Screens.Multi.Play api.Queue(request); await tcs.Task; - - return await base.SubmitScore(score); } protected override void Dispose(bool isDisposing) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index c8f1980ab1..a83f0e1b33 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -537,13 +537,23 @@ namespace osu.Game.Screens.Play try { - return await SubmitScore(score); + await SubmitScore(score); } catch (Exception ex) { Logger.Error(ex, "Score submission failed!"); - return score.ScoreInfo; } + + try + { + await ImportScore(score); + } + catch (Exception ex) + { + Logger.Error(ex, "Score import failed!"); + } + + return score.ScoreInfo; }); using (BeginDelayedSequence(RESULTS_DISPLAY_DELAY)) @@ -792,15 +802,15 @@ namespace osu.Game.Screens.Play } /// - /// Submits the player's . + /// Imports the player's to the local database. /// - /// The to submit. - /// The submitted score. - protected virtual async Task SubmitScore(Score score) + /// The to import. + /// The imported score. + protected virtual async Task ImportScore(Score score) { // Replays are already populated and present in the game's database, so should not be re-imported. if (DrawableRuleset.ReplayScore != null) - return score.ScoreInfo; + return; LegacyByteArrayReader replayReader; @@ -810,9 +820,16 @@ namespace osu.Game.Screens.Play replayReader = new LegacyByteArrayReader(stream.ToArray(), "replay.osr"); } - return await scoreManager.Import(score.ScoreInfo, replayReader); + await scoreManager.Import(score.ScoreInfo, replayReader); } + /// + /// Submits the player's . + /// + /// The to submit. + /// The submitted score. + protected virtual Task SubmitScore(Score score) => Task.CompletedTask; + /// /// Creates the for a . /// diff --git a/osu.Game/Screens/Play/ReplayPlayer.cs b/osu.Game/Screens/Play/ReplayPlayer.cs index 390d1d1959..a07213cb33 100644 --- a/osu.Game/Screens/Play/ReplayPlayer.cs +++ b/osu.Game/Screens/Play/ReplayPlayer.cs @@ -37,7 +37,8 @@ namespace osu.Game.Screens.Play return Score; } - protected override Task SubmitScore(Score score) => Task.FromResult(score.ScoreInfo); + // Don't re-import replay scores as they're already present in the database. + protected override Task ImportScore(Score score) => Task.CompletedTask; protected override ResultsScreen CreateResults(ScoreInfo score) => new SoloResultsScreen(score, false); From f1878eff639bce6d336008cc85e4a4b2c8260bf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 18 Dec 2020 23:45:42 +0100 Subject: [PATCH 47/60] Use yet another solution leveraging padding --- .../Edit/Compose/Components/SelectionBox.cs | 26 ++++++------------- 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs index 85e86499be..2f4721f63e 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.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.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -93,6 +92,7 @@ namespace osu.Game.Screens.Edit.Compose.Components } } + private Container dragHandles; private FillFlowContainer buttons; public const float BORDER_RADIUS = 3; @@ -152,6 +152,12 @@ namespace osu.Game.Screens.Edit.Compose.Components }, } }, + dragHandles = new Container + { + RelativeSizeAxes = Axes.Both, + // ensures that the centres of all drag handles line up with the middle of the selection box border. + Padding = new MarginPadding(BORDER_RADIUS / 2) + }, buttons = new FillFlowContainer { Y = 20, @@ -233,30 +239,14 @@ namespace osu.Game.Screens.Edit.Compose.Components }); } - private void addDragHandle(Anchor anchor) => AddInternal(new SelectionBoxDragHandle + private void addDragHandle(Anchor anchor) => dragHandles.Add(new SelectionBoxDragHandle { Anchor = anchor, - X = dragCircleAdjustments[anchor].X, - Y = dragCircleAdjustments[anchor].Y, HandleDrag = e => OnScale?.Invoke(e.Delta, anchor), OperationStarted = operationStarted, OperationEnded = operationEnded }); - /// - /// Adjust Drag circle to be centered on the center of the border instead of on the edge. - /// - private Dictionary dragCircleAdjustments = new Dictionary(){ - {Anchor.TopLeft, new Vector2(BORDER_RADIUS / 2)}, - {Anchor.CentreLeft, new Vector2(BORDER_RADIUS / 2, 0)}, - {Anchor.BottomLeft, new Vector2(BORDER_RADIUS / 2, -BORDER_RADIUS / 2)}, - {Anchor.TopCentre, new Vector2(0, BORDER_RADIUS / 2)}, - {Anchor.BottomCentre, new Vector2(0, -BORDER_RADIUS / 2)}, - {Anchor.TopRight, new Vector2(-BORDER_RADIUS / 2, BORDER_RADIUS / 2)}, - {Anchor.CentreRight, new Vector2(-BORDER_RADIUS / 2, 0)}, - {Anchor.BottomRight, new Vector2(-BORDER_RADIUS / 2)} - }; - private int activeOperations; private float convertDragEventToAngleOfRotation(DragEvent e) From beaced321153867f0ae9c1d39af5a82848e2ced0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 19 Dec 2020 13:58:56 +0900 Subject: [PATCH 48/60] Remove unnecessary async state machine --- osu.Game/Screens/Play/Player.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index a83f0e1b33..c539dff5d9 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -806,11 +806,11 @@ namespace osu.Game.Screens.Play /// /// The to import. /// The imported score. - protected virtual async Task ImportScore(Score score) + protected virtual Task ImportScore(Score score) { // Replays are already populated and present in the game's database, so should not be re-imported. if (DrawableRuleset.ReplayScore != null) - return; + return Task.CompletedTask; LegacyByteArrayReader replayReader; @@ -820,7 +820,7 @@ namespace osu.Game.Screens.Play replayReader = new LegacyByteArrayReader(stream.ToArray(), "replay.osr"); } - await scoreManager.Import(score.ScoreInfo, replayReader); + return scoreManager.Import(score.ScoreInfo, replayReader); } /// From 28ca21b432627ffb9d8058475bf969143dd71163 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 19 Dec 2020 14:50:09 +0100 Subject: [PATCH 49/60] Seal banned method & throw better exception --- osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs index aa668cc4c8..d2d5594edd 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs @@ -40,9 +40,9 @@ namespace osu.Game.Screens.Play.HUD return drawable; } - public override void Add(GameplayLeaderboardScore drawable) + public sealed override void Add(GameplayLeaderboardScore drawable) { - throw new InvalidOperationException($"Use {nameof(AddPlayer)} instead."); + throw new NotSupportedException($"Use {nameof(AddPlayer)} instead."); } private void sort() From 22a2c3efdf2f15944e7714da36ea11962345055d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 19 Dec 2020 14:55:58 +0100 Subject: [PATCH 50/60] Add back xmldoc of AddPlayer --- osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs index d2d5594edd..cab1cbd3f1 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs @@ -24,6 +24,14 @@ namespace osu.Game.Screens.Play.HUD LayoutEasing = Easing.OutQuint; } + /// + /// Adds a player to the leaderboard. + /// + /// The player. + /// + /// 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(User user, bool isTracked) { var drawable = new GameplayLeaderboardScore(user, isTracked) From d392e0f27e2444dfa8a21758a835ddbaef1842e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 19 Dec 2020 15:02:56 +0100 Subject: [PATCH 51/60] Extract shared rank-formatting helper --- osu.Game/Online/Leaderboards/LeaderboardScore.cs | 4 ++-- osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs | 3 +-- osu.Game/Utils/FormatUtils.cs | 8 ++++++++ 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/osu.Game/Online/Leaderboards/LeaderboardScore.cs b/osu.Game/Online/Leaderboards/LeaderboardScore.cs index dcd0cb435a..d8207aa8f4 100644 --- a/osu.Game/Online/Leaderboards/LeaderboardScore.cs +++ b/osu.Game/Online/Leaderboards/LeaderboardScore.cs @@ -24,8 +24,8 @@ using osu.Game.Scoring; using osu.Game.Users.Drawables; using osuTK; using osuTK.Graphics; -using Humanizer; using osu.Game.Online.API; +using osu.Game.Utils; namespace osu.Game.Online.Leaderboards { @@ -358,7 +358,7 @@ namespace osu.Game.Online.Leaderboards Anchor = Anchor.Centre, Origin = Anchor.Centre, Font = OsuFont.GetFont(size: 20, italics: true), - Text = rank == null ? "-" : rank.Value.ToMetric(decimals: rank < 100000 ? 1 : 0), + Text = rank == null ? "-" : rank.Value.FormatRank() }; } diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs index af3cb640bb..9684ae016c 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.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 Humanizer; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; @@ -49,7 +48,7 @@ namespace osu.Game.Screens.Play.HUD scorePosition = value; if (scorePosition.HasValue) - positionText.Text = $"#{scorePosition.Value.ToMetric(decimals: scorePosition < 100000 ? 1 : 0)}"; + positionText.Text = $"#{scorePosition.Value.FormatRank()}"; positionText.FadeTo(scorePosition.HasValue ? 1 : 0); updateColour(); diff --git a/osu.Game/Utils/FormatUtils.cs b/osu.Game/Utils/FormatUtils.cs index f2ab99f4b7..2578d8d835 100644 --- a/osu.Game/Utils/FormatUtils.cs +++ b/osu.Game/Utils/FormatUtils.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 Humanizer; + namespace osu.Game.Utils { public static class FormatUtils @@ -18,5 +20,11 @@ namespace osu.Game.Utils /// The accuracy to be formatted /// formatted accuracy in percentage public static string FormatAccuracy(this decimal accuracy) => $"{accuracy:0.00}%"; + + /// + /// Formats the supplied rank/leaderboard position in a consistent, simplified way. + /// + /// The rank/position to be formatted. + public static string FormatRank(this int rank) => rank.ToMetric(decimals: rank < 100_000 ? 1 : 0); } } From e2cc401c124b631c8ade6bde8120e71c44c3e2ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 19 Dec 2020 15:05:59 +0100 Subject: [PATCH 52/60] Move BDL above LoadComplete() --- .../Play/HUD/GameplayLeaderboardScore.cs | 106 +++++++++--------- 1 file changed, 53 insertions(+), 53 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs index 9684ae016c..d510ea5f8b 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs @@ -75,59 +75,6 @@ namespace osu.Game.Screens.Play.HUD Size = new Vector2(EXTENDED_WIDTH, PANEL_HEIGHT); } - protected override void LoadComplete() - { - base.LoadComplete(); - - updateColour(); - FinishTransforms(true); - } - - private const double transition_duration = 500; - - private void updateColour() - { - if (scorePosition == 1) - { - mainFillContainer.ResizeWidthTo(EXTENDED_WIDTH, transition_duration, Easing.OutElastic); - panelColour = Color4Extensions.FromHex("7fcc33"); - textColour = Color4.White; - } - else if (trackedPlayer) - { - mainFillContainer.ResizeWidthTo(EXTENDED_WIDTH, transition_duration, Easing.OutElastic); - panelColour = Color4Extensions.FromHex("ffd966"); - textColour = Color4Extensions.FromHex("2e576b"); - } - else - { - mainFillContainer.ResizeWidthTo(regular_width, transition_duration, Easing.OutElastic); - panelColour = Color4Extensions.FromHex("3399cc"); - textColour = Color4.White; - } - } - - private Color4 panelColour - { - set - { - mainFillContainer.FadeColour(value, transition_duration, Easing.OutQuint); - centralFill.FadeColour(value, transition_duration, Easing.OutQuint); - } - } - - private Color4 textColour - { - set - { - scoreText.FadeColour(value, 200, Easing.OutQuint); - accuracyText.FadeColour(value, 200, Easing.OutQuint); - comboText.FadeColour(value, 200, Easing.OutQuint); - usernameText.FadeColour(value, 200, Easing.OutQuint); - positionText.FadeColour(value, 200, Easing.OutQuint); - } - } - [BackgroundDependencyLoader] private void load(OsuColour colours) { @@ -284,5 +231,58 @@ namespace osu.Game.Screens.Play.HUD Accuracy.BindValueChanged(v => accuracyText.Text = v.NewValue.FormatAccuracy(), true); Combo.BindValueChanged(v => comboText.Text = $"{v.NewValue}x", true); } + + protected override void LoadComplete() + { + base.LoadComplete(); + + updateColour(); + FinishTransforms(true); + } + + private const double transition_duration = 500; + + private void updateColour() + { + if (scorePosition == 1) + { + mainFillContainer.ResizeWidthTo(EXTENDED_WIDTH, transition_duration, Easing.OutElastic); + panelColour = Color4Extensions.FromHex("7fcc33"); + textColour = Color4.White; + } + else if (trackedPlayer) + { + mainFillContainer.ResizeWidthTo(EXTENDED_WIDTH, transition_duration, Easing.OutElastic); + panelColour = Color4Extensions.FromHex("ffd966"); + textColour = Color4Extensions.FromHex("2e576b"); + } + else + { + mainFillContainer.ResizeWidthTo(regular_width, transition_duration, Easing.OutElastic); + panelColour = Color4Extensions.FromHex("3399cc"); + textColour = Color4.White; + } + } + + private Color4 panelColour + { + set + { + mainFillContainer.FadeColour(value, transition_duration, Easing.OutQuint); + centralFill.FadeColour(value, transition_duration, Easing.OutQuint); + } + } + + private Color4 textColour + { + set + { + scoreText.FadeColour(value, 200, Easing.OutQuint); + accuracyText.FadeColour(value, 200, Easing.OutQuint); + comboText.FadeColour(value, 200, Easing.OutQuint); + usernameText.FadeColour(value, 200, Easing.OutQuint); + positionText.FadeColour(value, 200, Easing.OutQuint); + } + } } } From 315a957a0ca3ad242671e49ec73a27e05e1432b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 19 Dec 2020 15:17:31 +0100 Subject: [PATCH 53/60] Extract constant for text transition duration --- .../Screens/Play/HUD/GameplayLeaderboardScore.cs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs index d510ea5f8b..cff3a1fc66 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs @@ -273,15 +273,17 @@ namespace osu.Game.Screens.Play.HUD } } + private const double text_transition_duration = 200; + private Color4 textColour { set { - scoreText.FadeColour(value, 200, Easing.OutQuint); - accuracyText.FadeColour(value, 200, Easing.OutQuint); - comboText.FadeColour(value, 200, Easing.OutQuint); - usernameText.FadeColour(value, 200, Easing.OutQuint); - positionText.FadeColour(value, 200, Easing.OutQuint); + scoreText.FadeColour(value, text_transition_duration, Easing.OutQuint); + accuracyText.FadeColour(value, text_transition_duration, Easing.OutQuint); + comboText.FadeColour(value, text_transition_duration, Easing.OutQuint); + usernameText.FadeColour(value, text_transition_duration, Easing.OutQuint); + positionText.FadeColour(value, text_transition_duration, Easing.OutQuint); } } } From 06a17a9d8cb9c94dd886e2b1e80992436521f42d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 19 Dec 2020 15:18:05 +0100 Subject: [PATCH 54/60] Rename other constant to be distinguishable --- .../Screens/Play/HUD/GameplayLeaderboardScore.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs index cff3a1fc66..b93db09f71 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs @@ -240,25 +240,25 @@ namespace osu.Game.Screens.Play.HUD FinishTransforms(true); } - private const double transition_duration = 500; + private const double panel_transition_duration = 500; private void updateColour() { if (scorePosition == 1) { - mainFillContainer.ResizeWidthTo(EXTENDED_WIDTH, transition_duration, Easing.OutElastic); + mainFillContainer.ResizeWidthTo(EXTENDED_WIDTH, panel_transition_duration, Easing.OutElastic); panelColour = Color4Extensions.FromHex("7fcc33"); textColour = Color4.White; } else if (trackedPlayer) { - mainFillContainer.ResizeWidthTo(EXTENDED_WIDTH, transition_duration, Easing.OutElastic); + mainFillContainer.ResizeWidthTo(EXTENDED_WIDTH, panel_transition_duration, Easing.OutElastic); panelColour = Color4Extensions.FromHex("ffd966"); textColour = Color4Extensions.FromHex("2e576b"); } else { - mainFillContainer.ResizeWidthTo(regular_width, transition_duration, Easing.OutElastic); + mainFillContainer.ResizeWidthTo(regular_width, panel_transition_duration, Easing.OutElastic); panelColour = Color4Extensions.FromHex("3399cc"); textColour = Color4.White; } @@ -268,8 +268,8 @@ namespace osu.Game.Screens.Play.HUD { set { - mainFillContainer.FadeColour(value, transition_duration, Easing.OutQuint); - centralFill.FadeColour(value, transition_duration, Easing.OutQuint); + mainFillContainer.FadeColour(value, panel_transition_duration, Easing.OutQuint); + centralFill.FadeColour(value, panel_transition_duration, Easing.OutQuint); } } From c738a57b399b5f701fa314fb2dc01de43659c092 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 19 Dec 2020 18:48:17 +0100 Subject: [PATCH 55/60] Fix username overflow in new leaderboard design --- osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs index b93db09f71..58281debf1 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs @@ -176,7 +176,7 @@ namespace osu.Game.Screens.Play.HUD usernameText = new OsuSpriteText { RelativeSizeAxes = Axes.X, - Width = 0.8f, + Width = 0.6f, Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, Colour = Color4.White, From afa6a869545692f4abed538f7d5e20e70a37a3c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 19 Dec 2020 19:00:05 +0100 Subject: [PATCH 56/60] Remove unnecessary lookup incrementing --- .../Visual/Online/TestSceneCurrentlyPlayingDisplay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneCurrentlyPlayingDisplay.cs b/osu.Game.Tests/Visual/Online/TestSceneCurrentlyPlayingDisplay.cs index eaa881b02c..1666c9cde4 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneCurrentlyPlayingDisplay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneCurrentlyPlayingDisplay.cs @@ -92,7 +92,7 @@ namespace osu.Game.Tests.Visual.Online protected override Task ComputeValueAsync(int lookup, CancellationToken token = default) => Task.FromResult(new User { - Id = lookup++, + Id = lookup, Username = usernames[lookup % usernames.Length], }); } From ee33c0be93c16b61762edc6d85165fb5ec05fd93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 19 Dec 2020 19:08:29 +0100 Subject: [PATCH 57/60] Extract combo & accuracy ratio calculation helpers --- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index f5fb918ba3..10d0cc2865 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -201,8 +201,8 @@ namespace osu.Game.Rulesets.Scoring private double getScore(ScoringMode mode) { return GetScore(mode, maxAchievableCombo, - maxBaseScore > 0 ? baseScore / maxBaseScore : 0, - maxAchievableCombo > 0 ? (double)HighestCombo.Value / maxAchievableCombo : 1, + calculateAccuracyRatio(baseScore), + calculateComboRatio(HighestCombo.Value), scoreResultCounts); } @@ -252,14 +252,17 @@ namespace osu.Game.Rulesets.Scoring computedBaseScore += Judgement.ToNumericResult(pair.Key) * pair.Value; } - double accuracy = maxBaseScore > 0 ? computedBaseScore / maxBaseScore : 0; - double comboRatio = maxAchievableCombo > 0 ? (double)maxCombo / maxAchievableCombo : 1; + double accuracy = calculateAccuracyRatio(computedBaseScore); + double comboRatio = calculateComboRatio(maxCombo); double score = GetScore(mode, maxAchievableCombo, accuracy, comboRatio, scoreResultCounts); return (score, accuracy); } + private double calculateAccuracyRatio(double baseScore) => maxBaseScore > 0 ? baseScore / maxBaseScore : 0; + 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; From 631a0cea41e7e21b1ab592bc80fa748c733a22d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 19 Dec 2020 21:25:04 +0100 Subject: [PATCH 58/60] Fix intended random factor not being random in test --- .../Visual/Gameplay/TestSceneMultiplayerGameplayLeaderboard.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneMultiplayerGameplayLeaderboard.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneMultiplayerGameplayLeaderboard.cs index 0c8d8ca165..0ca1d9008b 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneMultiplayerGameplayLeaderboard.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneMultiplayerGameplayLeaderboard.cs @@ -109,7 +109,7 @@ namespace osu.Game.Tests.Visual.Gameplay { foreach (var userId in PlayingUsers) { - if (RNG.Next(0, 1) == 1) + if (RNG.NextBool()) continue; if (!lastHeaders.TryGetValue(userId, out var header)) From f827b6c030b3cbde588effdd908b3138eff827fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 19 Dec 2020 21:26:40 +0100 Subject: [PATCH 59/60] Use terser dictionary initialiser syntax --- .../TestSceneMultiplayerGameplayLeaderboard.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneMultiplayerGameplayLeaderboard.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneMultiplayerGameplayLeaderboard.cs index 0ca1d9008b..e42ddeb35e 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneMultiplayerGameplayLeaderboard.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneMultiplayerGameplayLeaderboard.cs @@ -116,12 +116,12 @@ namespace osu.Game.Tests.Visual.Gameplay { lastHeaders[userId] = header = new FrameHeader(new ScoreInfo { - Statistics = new Dictionary(new[] + Statistics = new Dictionary { - new KeyValuePair(HitResult.Miss, 0), - new KeyValuePair(HitResult.Meh, 0), - new KeyValuePair(HitResult.Great, 0) - }) + [HitResult.Miss] = 0, + [HitResult.Meh] = 0, + [HitResult.Great] = 0 + } }); } From 4e5064c4f611de5704982e8dbbe670a247814f23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 19 Dec 2020 21:31:17 +0100 Subject: [PATCH 60/60] Start accuracy at 1 --- 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 c96f496cd0..12321de442 100644 --- a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs @@ -108,7 +108,7 @@ namespace osu.Game.Screens.Play.HUD public IBindableNumber Accuracy => accuracy; - private readonly BindableDouble accuracy = new BindableDouble(); + private readonly BindableDouble accuracy = new BindableDouble(1); public IBindableNumber CurrentCombo => currentCombo;