From ebee9e68886925a368e87596c0e68d03c3fe025f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 Apr 2022 11:04:53 +0900 Subject: [PATCH 01/11] Fix `MultiplayerGameplayLeaderboard` not immediately updating totals on scoring mode change --- 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 4f5edab526..d0726248aa 100644 --- a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs @@ -75,7 +75,10 @@ namespace osu.Game.Screens.Play.HUD foreach (var user in playingUsers) { var trackedUser = CreateUserData(user, ruleset, scoreProcessor); + trackedUser.ScoringMode.BindTo(scoringMode); + trackedUser.Score.BindValueChanged(_ => Scheduler.AddOnce(updateTotals)); + UserScores[user.UserID] = trackedUser; if (trackedUser.Team is int team && !TeamScores.ContainsKey(team)) @@ -175,8 +178,6 @@ namespace osu.Game.Screens.Play.HUD trackedData.Frames.Add(new TimedFrame(bundle.Frames.First().Time, bundle.Header)); trackedData.UpdateScore(); - - updateTotals(); }); private void updateTotals() From 577e29351ef5cf6e64336572b71a35214d94a1c6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 Apr 2022 11:46:21 +0900 Subject: [PATCH 02/11] Ensure players are always on both leaderboard teams In a very rare case, the randomisation may cause all users to be on one team, causing a test failure. The odds make it basically impossible, but if adjusting the number of users in the test scene this can more readily be hit. --- .../TestSceneMultiplayerGameplayLeaderboardTeams.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs index 5cd3bb2f0f..5caab9487e 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs @@ -3,7 +3,6 @@ using System.Linq; using osu.Framework.Graphics; -using osu.Framework.Utils; using osu.Game.Online.Multiplayer; using osu.Game.Online.Multiplayer.MatchTypes.TeamVersus; using osu.Game.Rulesets.Osu.Scoring; @@ -14,12 +13,14 @@ namespace osu.Game.Tests.Visual.Multiplayer { public class TestSceneMultiplayerGameplayLeaderboardTeams : MultiplayerGameplayLeaderboardTestScene { + private int team; + protected override MultiplayerRoomUser CreateUser(int userId) { var user = base.CreateUser(userId); user.MatchState = new TeamVersusUserState { - TeamID = RNG.Next(0, 2) + TeamID = team++ % 2 }; return user; } From 0ba95a448306c888163b442a2c8bb8c6b3768c23 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 Apr 2022 11:54:30 +0900 Subject: [PATCH 03/11] Ensure all users are shown on leaderboard (even when API lookup fails) --- .../Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs index d0726248aa..11498a02cd 100644 --- a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs @@ -89,10 +89,13 @@ namespace osu.Game.Screens.Play.HUD { var users = task.GetResultSafely(); - foreach (var user in users) + for (int i = 0; i < users.Length; i++) { - if (user == null) - continue; + var user = users[i] ??= new APIUser + { + Id = playingUsers[i].UserID, + Username = "Unknown user", + }; var trackedUser = UserScores[user.Id]; From f29e32970003986caf7297139b3fa3c57c6f7a2d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 Apr 2022 12:11:51 +0900 Subject: [PATCH 04/11] Move user count to a constant to allow easier adjustment --- .../Multiplayer/MultiplayerGameplayLeaderboardTestScene.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/MultiplayerGameplayLeaderboardTestScene.cs b/osu.Game.Tests/Visual/Multiplayer/MultiplayerGameplayLeaderboardTestScene.cs index 98e6cd514b..ab187a2f76 100644 --- a/osu.Game.Tests/Visual/Multiplayer/MultiplayerGameplayLeaderboardTestScene.cs +++ b/osu.Game.Tests/Visual/Multiplayer/MultiplayerGameplayLeaderboardTestScene.cs @@ -27,6 +27,8 @@ namespace osu.Game.Tests.Visual.Multiplayer { public abstract class MultiplayerGameplayLeaderboardTestScene : OsuTestScene { + private const int total_users = 16; + protected readonly BindableList MultiplayerUsers = new BindableList(); protected MultiplayerGameplayLeaderboard Leaderboard { get; private set; } @@ -94,7 +96,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("populate users", () => { MultiplayerUsers.Clear(); - for (int i = 0; i < 16; i++) + for (int i = 0; i < total_users; i++) MultiplayerUsers.Add(CreateUser(i)); }); From 22c75a518ed4fd35f59eaa0fa1a98a4f404b1b53 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 Apr 2022 12:38:15 +0900 Subject: [PATCH 05/11] Fix headers not getting reset on re-run of test --- .../Multiplayer/MultiplayerGameplayLeaderboardTestScene.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/MultiplayerGameplayLeaderboardTestScene.cs b/osu.Game.Tests/Visual/Multiplayer/MultiplayerGameplayLeaderboardTestScene.cs index ab187a2f76..4e6342868a 100644 --- a/osu.Game.Tests/Visual/Multiplayer/MultiplayerGameplayLeaderboardTestScene.cs +++ b/osu.Game.Tests/Visual/Multiplayer/MultiplayerGameplayLeaderboardTestScene.cs @@ -86,7 +86,11 @@ namespace osu.Game.Tests.Visual.Multiplayer [SetUpSteps] public virtual void SetUpSteps() { - AddStep("reset counts", () => spectatorClient.Invocations.Clear()); + AddStep("reset counts", () => + { + spectatorClient.Invocations.Clear(); + lastHeaders.Clear(); + }); AddStep("set local user", () => ((DummyAPIAccess)API).LocalUser.Value = new APIUser { From 8b1cee75faa0b855d29a4d288f773d03cba348b3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 Apr 2022 14:13:07 +0900 Subject: [PATCH 06/11] Use `BindableLong` instead of `BindableInt` for user score tracking --- .../Visual/Multiplayer/TestSceneMultiplayerTeamResults.cs | 6 +++--- .../OnlinePlay/Multiplayer/MultiplayerTeamResultsScreen.cs | 4 ++-- osu.Game/Screens/Play/HUD/MatchScoreDisplay.cs | 6 +++--- osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerTeamResults.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerTeamResults.cs index bdc348b043..bcb36a585f 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerTeamResults.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerTeamResults.cs @@ -26,10 +26,10 @@ namespace osu.Game.Tests.Visual.Multiplayer var beatmapInfo = CreateBeatmap(rulesetInfo).BeatmapInfo; var score = TestResources.CreateTestScoreInfo(beatmapInfo); - SortedDictionary teamScores = new SortedDictionary + SortedDictionary teamScores = new SortedDictionary { - { 0, new BindableInt(team1Score) }, - { 1, new BindableInt(team2Score) } + { 0, new BindableLong(team1Score) }, + { 1, new BindableLong(team2Score) } }; Stack.Push(screen = new MultiplayerTeamResultsScreen(score, 1, new PlaylistItem(beatmapInfo), teamScores)); diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerTeamResultsScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerTeamResultsScreen.cs index 14a779dedf..3f0f3e043c 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerTeamResultsScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerTeamResultsScreen.cs @@ -24,12 +24,12 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer { public class MultiplayerTeamResultsScreen : MultiplayerResultsScreen { - private readonly SortedDictionary teamScores; + private readonly SortedDictionary teamScores; private Container winnerBackground; private Drawable winnerText; - public MultiplayerTeamResultsScreen(ScoreInfo score, long roomId, PlaylistItem playlistItem, SortedDictionary teamScores) + public MultiplayerTeamResultsScreen(ScoreInfo score, long roomId, PlaylistItem playlistItem, SortedDictionary teamScores) : base(score, roomId, playlistItem) { if (teamScores.Count != 2) diff --git a/osu.Game/Screens/Play/HUD/MatchScoreDisplay.cs b/osu.Game/Screens/Play/HUD/MatchScoreDisplay.cs index 88cf9529bf..2129000268 100644 --- a/osu.Game/Screens/Play/HUD/MatchScoreDisplay.cs +++ b/osu.Game/Screens/Play/HUD/MatchScoreDisplay.cs @@ -19,8 +19,8 @@ namespace osu.Game.Screens.Play.HUD private const float bar_height = 18; private const float font_size = 50; - public BindableInt Team1Score = new BindableInt(); - public BindableInt Team2Score = new BindableInt(); + public BindableLong Team1Score = new BindableLong(); + public BindableLong Team2Score = new BindableLong(); protected MatchScoreCounter Score1Text; protected MatchScoreCounter Score2Text; @@ -133,7 +133,7 @@ namespace osu.Game.Screens.Play.HUD var winningBar = Team1Score.Value > Team2Score.Value ? score1Bar : score2Bar; var losingBar = Team1Score.Value <= Team2Score.Value ? score1Bar : score2Bar; - int diff = Math.Max(Team1Score.Value, Team2Score.Value) - Math.Min(Team1Score.Value, Team2Score.Value); + long diff = Math.Max(Team1Score.Value, Team2Score.Value) - Math.Min(Team1Score.Value, Team2Score.Value); losingBar.ResizeWidthTo(0, 400, Easing.OutQuint); winningBar.ResizeWidthTo(Math.Min(0.4f, MathF.Pow(diff / 1500000f, 0.5f) / 2), 400, Easing.OutQuint); diff --git a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs index 11498a02cd..e569e5368b 100644 --- a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs @@ -29,7 +29,7 @@ namespace osu.Game.Screens.Play.HUD { protected readonly Dictionary UserScores = new Dictionary(); - public readonly SortedDictionary TeamScores = new SortedDictionary(); + public readonly SortedDictionary TeamScores = new SortedDictionary(); [Resolved] private OsuColour colours { get; set; } @@ -82,7 +82,7 @@ namespace osu.Game.Screens.Play.HUD UserScores[user.UserID] = trackedUser; if (trackedUser.Team is int team && !TeamScores.ContainsKey(team)) - TeamScores.Add(team, new BindableInt()); + TeamScores.Add(team, new BindableLong()); } userLookupCache.GetUsersAsync(playingUsers.Select(u => u.UserID).ToArray()).ContinueWith(task => Schedule(() => From 45d79c1a737a8e6128e1ce631b5e37185fd48d9f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 Apr 2022 18:01:29 +0900 Subject: [PATCH 07/11] Ensure previous test score is cleaned up to allow visual test runs to work better --- .../Gameplay/TestSceneReplayDownloadButton.cs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs index 5d25287e45..e89350de1a 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs @@ -26,6 +26,8 @@ namespace osu.Game.Tests.Visual.Gameplay [TestFixture] public class TestSceneReplayDownloadButton : OsuManualInputManagerTestScene { + private const long online_score_id = 2553163309; + [Resolved] private RulesetStore rulesets { get; set; } @@ -43,6 +45,15 @@ namespace osu.Game.Tests.Visual.Gameplay beatmapManager.Import(TestResources.GetQuickTestBeatmapForImport()).WaitSafely(); } + [SetUpSteps] + public void SetUpSteps() + { + AddStep("delete previous imports", () => + { + scoreManager.Delete(s => s.OnlineID == online_score_id); + }); + } + [Test] public void TestDisplayStates() { @@ -180,7 +191,7 @@ namespace osu.Game.Tests.Visual.Gameplay { return new APIScore { - OnlineID = 2553163309, + OnlineID = online_score_id, RulesetID = 0, Beatmap = CreateAPIBeatmapSet(new OsuRuleset().RulesetInfo).Beatmaps.First(), HasReplay = replayAvailable, From b51abfc72220646e1af51663fdc317bf58b0b3bd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 Apr 2022 18:01:13 +0900 Subject: [PATCH 08/11] Fix `ScoreDownloadTracker` matching on empty hash equality --- osu.Game/Online/ScoreDownloadTracker.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Online/ScoreDownloadTracker.cs b/osu.Game/Online/ScoreDownloadTracker.cs index d7e31c8a59..ed1c566dbe 100644 --- a/osu.Game/Online/ScoreDownloadTracker.cs +++ b/osu.Game/Online/ScoreDownloadTracker.cs @@ -47,7 +47,10 @@ namespace osu.Game.Online Downloader.DownloadBegan += downloadBegan; Downloader.DownloadFailed += downloadFailed; - realmSubscription = realm.RegisterForNotifications(r => r.All().Where(s => ((s.OnlineID > 0 && s.OnlineID == TrackedItem.OnlineID) || s.Hash == TrackedItem.Hash) && !s.DeletePending), (items, changes, ___) => + realmSubscription = realm.RegisterForNotifications(r => r.All().Where(s => + ((s.OnlineID > 0 && s.OnlineID == TrackedItem.OnlineID) + || (!string.IsNullOrEmpty(s.Hash) && s.Hash == TrackedItem.Hash)) + && !s.DeletePending), (items, changes, ___) => { if (items.Any()) Schedule(() => UpdateState(DownloadState.LocallyAvailable)); From 6dbfc261585fd239e8b7b13a6f400938f581e238 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 Apr 2022 18:07:55 +0900 Subject: [PATCH 09/11] Add log output when a model is undeleted --- osu.Game/Stores/RealmArchiveModelImporter.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Stores/RealmArchiveModelImporter.cs b/osu.Game/Stores/RealmArchiveModelImporter.cs index 1d0e16d549..353a2164bb 100644 --- a/osu.Game/Stores/RealmArchiveModelImporter.cs +++ b/osu.Game/Stores/RealmArchiveModelImporter.cs @@ -389,7 +389,10 @@ namespace osu.Game.Stores LogForModel(item, @$"Found existing {HumanisedModelName} for {item} (ID {existing.ID}) – skipping import."); if (existing.DeletePending) + { + LogForModel(item, $@"Existing {HumanisedModelName}'s deletion flag has been removed"); UndeleteForReuse(existing); + } transaction.Commit(); From 017f3852c8a8354b08cb6a3f22a1624dea4b45db Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Apr 2022 13:32:34 +0900 Subject: [PATCH 10/11] Replace incorrectly chosen `??=` with `??` --- 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 e569e5368b..41b40e9a91 100644 --- a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs @@ -91,7 +91,7 @@ namespace osu.Game.Screens.Play.HUD for (int i = 0; i < users.Length; i++) { - var user = users[i] ??= new APIUser + var user = users[i] ?? new APIUser { Id = playingUsers[i].UserID, Username = "Unknown user", From 56427becbba54e042c167edf8c59b03e37136aba Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Apr 2022 14:33:28 +0900 Subject: [PATCH 11/11] Move logging and early return into `UndeleteForReuse` method itself --- osu.Game/Stores/RealmArchiveModelImporter.cs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/osu.Game/Stores/RealmArchiveModelImporter.cs b/osu.Game/Stores/RealmArchiveModelImporter.cs index 353a2164bb..6d1449a4b4 100644 --- a/osu.Game/Stores/RealmArchiveModelImporter.cs +++ b/osu.Game/Stores/RealmArchiveModelImporter.cs @@ -351,8 +351,7 @@ namespace osu.Game.Stores using (var transaction = realm.BeginWrite()) { - if (existing.DeletePending) - UndeleteForReuse(existing); + UndeleteForReuse(existing); transaction.Commit(); } @@ -388,12 +387,7 @@ namespace osu.Game.Stores { LogForModel(item, @$"Found existing {HumanisedModelName} for {item} (ID {existing.ID}) – skipping import."); - if (existing.DeletePending) - { - LogForModel(item, $@"Existing {HumanisedModelName}'s deletion flag has been removed"); - UndeleteForReuse(existing); - } - + UndeleteForReuse(existing); transaction.Commit(); return existing.ToLive(Realm); @@ -539,6 +533,10 @@ namespace osu.Game.Stores /// The existing model. protected virtual void UndeleteForReuse(TModel existing) { + if (!existing.DeletePending) + return; + + LogForModel(existing, $@"Existing {HumanisedModelName}'s deletion flag has been removed to allow for reuse."); existing.DeletePending = false; }