diff --git a/osu.Game.Tests/Visual/Online/TestSceneSoloStatisticsWatcher.cs b/osu.Game.Tests/Visual/Online/TestSceneSoloStatisticsWatcher.cs index e62e53bd02..be819afa3e 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneSoloStatisticsWatcher.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneSoloStatisticsWatcher.cs @@ -268,6 +268,26 @@ namespace osu.Game.Tests.Visual.Online AddAssert("update not received", () => update == null); } + [Test] + public void TestGlobalStatisticsUpdatedAfterRegistrationAddedAndScoreProcessed() + { + int userId = getUserId(); + long scoreId = getScoreId(); + setUpUser(userId); + + var ruleset = new OsuRuleset().RulesetInfo; + + SoloStatisticsUpdate? update = null; + registerForUpdates(scoreId, ruleset, receivedUpdate => update = receivedUpdate); + + feignScoreProcessing(userId, ruleset, 5_000_000); + + AddStep("signal score processed", () => ((ISpectatorClient)spectatorClient).UserScoreProcessed(userId, scoreId)); + AddUntilStep("update received", () => update != null); + AddAssert("local user values are correct", () => dummyAPI.LocalUser.Value.Statistics.TotalScore, () => Is.EqualTo(5_000_000)); + AddAssert("statistics values are correct", () => dummyAPI.Statistics.Value!.TotalScore, () => Is.EqualTo(5_000_000)); + } + private int nextUserId = 2000; private long nextScoreId = 50000; diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs index be5bdeca77..17bf8bcc37 100644 --- a/osu.Game/Online/API/APIAccess.cs +++ b/osu.Game/Online/API/APIAccess.cs @@ -53,6 +53,7 @@ namespace osu.Game.Online.API public IBindable LocalUser => localUser; public IBindableList Friends => friends; public IBindable Activity => activity; + public IBindable Statistics => statistics; public Language Language => game.CurrentLanguage.Value; @@ -65,6 +66,8 @@ namespace osu.Game.Online.API private Bindable configStatus { get; } = new Bindable(); private Bindable localUserStatus { get; } = new Bindable(); + private Bindable statistics { get; } = new Bindable(); + protected bool HasLogin => authentication.Token.Value != null || (!string.IsNullOrEmpty(ProvidedUsername) && !string.IsNullOrEmpty(password)); private readonly CancellationTokenSource cancellationToken = new CancellationTokenSource(); @@ -517,9 +520,21 @@ namespace osu.Game.Online.API flushQueue(); } + public void UpdateStatistics(UserStatistics newStatistics) + { + statistics.Value = newStatistics; + + if (IsLoggedIn) + localUser.Value.Statistics = newStatistics; + } + private static APIUser createGuestUser() => new GuestUser(); - private void setLocalUser(APIUser user) => Scheduler.Add(() => localUser.Value = user, false); + private void setLocalUser(APIUser user) => Scheduler.Add(() => + { + localUser.Value = user; + statistics.Value = user.Statistics; + }, false); protected override void Dispose(bool isDisposing) { diff --git a/osu.Game/Online/API/DummyAPIAccess.cs b/osu.Game/Online/API/DummyAPIAccess.cs index d585124db6..4b4f8061e0 100644 --- a/osu.Game/Online/API/DummyAPIAccess.cs +++ b/osu.Game/Online/API/DummyAPIAccess.cs @@ -28,6 +28,8 @@ namespace osu.Game.Online.API public Bindable Activity { get; } = new Bindable(); + public Bindable Statistics { get; } = new Bindable(); + public Language Language => Language.en; public string AccessToken => "token"; @@ -115,6 +117,12 @@ namespace osu.Game.Online.API Id = DUMMY_USER_ID, }; + Statistics.Value = new UserStatistics + { + GlobalRank = 1, + CountryRank = 1 + }; + state.Value = APIState.Online; } @@ -126,6 +134,14 @@ namespace osu.Game.Online.API LocalUser.Value = new GuestUser(); } + public void UpdateStatistics(UserStatistics newStatistics) + { + Statistics.Value = newStatistics; + + if (IsLoggedIn) + LocalUser.Value.Statistics = newStatistics; + } + public IHubClientConnector? GetHubConnector(string clientName, string endpoint, bool preferMessagePack) => null; public NotificationsClientConnector GetNotificationsConnector() => new PollingNotificationsClientConnector(this); @@ -141,6 +157,7 @@ namespace osu.Game.Online.API IBindable IAPIProvider.LocalUser => LocalUser; IBindableList IAPIProvider.Friends => Friends; IBindable IAPIProvider.Activity => Activity; + IBindable IAPIProvider.Statistics => Statistics; /// /// During the next simulated login, the process will fail immediately. diff --git a/osu.Game/Online/API/IAPIProvider.cs b/osu.Game/Online/API/IAPIProvider.cs index a1d7006c8c..b58d4a363a 100644 --- a/osu.Game/Online/API/IAPIProvider.cs +++ b/osu.Game/Online/API/IAPIProvider.cs @@ -28,6 +28,11 @@ namespace osu.Game.Online.API /// IBindable Activity { get; } + /// + /// The current user's online statistics. + /// + IBindable Statistics { get; } + /// /// The language supplied by this provider to API requests. /// @@ -111,6 +116,11 @@ namespace osu.Game.Online.API /// void Logout(); + /// + /// Sets Statistics bindable. + /// + void UpdateStatistics(UserStatistics newStatistics); + /// /// Constructs a new . May be null if not supported. /// diff --git a/osu.Game/Online/Solo/SoloStatisticsWatcher.cs b/osu.Game/Online/Solo/SoloStatisticsWatcher.cs index 46449fea73..55b27fb364 100644 --- a/osu.Game/Online/Solo/SoloStatisticsWatcher.cs +++ b/osu.Game/Online/Solo/SoloStatisticsWatcher.cs @@ -127,6 +127,8 @@ namespace osu.Game.Online.Solo { string rulesetName = callback.Score.Ruleset.ShortName; + api.UpdateStatistics(updatedStatistics); + if (latestStatistics == null) return;