mirror of https://github.com/ppy/osu
Decouple game-wide ruleset bindable and refactor `LocalUserStatisticsProvider`
This also throws away the logic of updating `API.LocalUser.Value.Statistics`. Components should rely on `LocalUserStatisticsProvider` instead for proper behaviour and ability to update on statistics updates.
This commit is contained in:
parent
6c8a900dcc
commit
4a628287e2
|
@ -69,7 +69,7 @@ internal partial class DiscordRichPresence : Component
|
|||
};
|
||||
|
||||
private IBindable<APIUser>? user;
|
||||
private IBindable<UserStatistics?>? localStatistics;
|
||||
private IBindable<UserStatisticsUpdate>? statisticsUpdate;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
|
@ -123,8 +123,8 @@ protected override void LoadComplete()
|
|||
activity.BindValueChanged(_ => schedulePresenceUpdate());
|
||||
privacyMode.BindValueChanged(_ => schedulePresenceUpdate());
|
||||
|
||||
localStatistics = statisticsProvider.Statistics.GetBoundCopy();
|
||||
localStatistics.BindValueChanged(_ => schedulePresenceUpdate());
|
||||
statisticsUpdate = statisticsProvider.StatisticsUpdate.GetBoundCopy();
|
||||
statisticsUpdate.BindValueChanged(_ => schedulePresenceUpdate());
|
||||
|
||||
multiplayerClient.RoomUpdated += onRoomUpdated;
|
||||
}
|
||||
|
@ -167,7 +167,7 @@ private void schedulePresenceUpdate()
|
|||
|
||||
private void updatePresence(bool hideIdentifiableInformation)
|
||||
{
|
||||
if (user == null || localStatistics == null)
|
||||
if (user == null)
|
||||
return;
|
||||
|
||||
// user activity
|
||||
|
@ -237,7 +237,10 @@ private void updatePresence(bool hideIdentifiableInformation)
|
|||
if (privacyMode.Value == DiscordRichPresenceMode.Limited)
|
||||
presence.Assets.LargeImageText = string.Empty;
|
||||
else
|
||||
presence.Assets.LargeImageText = $"{user.Value.Username}" + (localStatistics.Value?.GlobalRank > 0 ? $" (rank #{localStatistics.Value?.GlobalRank:N0})" : string.Empty);
|
||||
{
|
||||
var statistics = statisticsProvider.GetStatisticsFor(ruleset.Value);
|
||||
presence.Assets.LargeImageText = $"{user.Value.Username}" + (statistics?.GlobalRank > 0 ? $" (rank #{statistics.GlobalRank:N0})" : string.Empty);
|
||||
}
|
||||
|
||||
// small image
|
||||
presence.Assets.SmallImageKey = ruleset.Value.IsLegacyRuleset() ? $"mode_{ruleset.Value.OnlineID}" : "mode_custom";
|
||||
|
|
|
@ -101,7 +101,7 @@ public void TestTransientUserStatisticsDisplay()
|
|||
AddStep("Gain", () =>
|
||||
{
|
||||
var transientUpdateDisplay = this.ChildrenOfType<TransientUserStatisticsUpdateDisplay>().Single();
|
||||
transientUpdateDisplay.LatestUpdate.Value = new UserStatisticsUpdate(
|
||||
transientUpdateDisplay.LatestUpdate.Value = new ScoreBasedUserStatisticsUpdate(
|
||||
new ScoreInfo(),
|
||||
new UserStatistics
|
||||
{
|
||||
|
@ -118,7 +118,7 @@ public void TestTransientUserStatisticsDisplay()
|
|||
AddStep("Loss", () =>
|
||||
{
|
||||
var transientUpdateDisplay = this.ChildrenOfType<TransientUserStatisticsUpdateDisplay>().Single();
|
||||
transientUpdateDisplay.LatestUpdate.Value = new UserStatisticsUpdate(
|
||||
transientUpdateDisplay.LatestUpdate.Value = new ScoreBasedUserStatisticsUpdate(
|
||||
new ScoreInfo(),
|
||||
new UserStatistics
|
||||
{
|
||||
|
@ -136,7 +136,7 @@ public void TestTransientUserStatisticsDisplay()
|
|||
AddStep("Tiny increase in PP", () =>
|
||||
{
|
||||
var transientUpdateDisplay = this.ChildrenOfType<TransientUserStatisticsUpdateDisplay>().Single();
|
||||
transientUpdateDisplay.LatestUpdate.Value = new UserStatisticsUpdate(
|
||||
transientUpdateDisplay.LatestUpdate.Value = new ScoreBasedUserStatisticsUpdate(
|
||||
new ScoreInfo(),
|
||||
new UserStatistics
|
||||
{
|
||||
|
@ -153,7 +153,7 @@ public void TestTransientUserStatisticsDisplay()
|
|||
AddStep("No change 1", () =>
|
||||
{
|
||||
var transientUpdateDisplay = this.ChildrenOfType<TransientUserStatisticsUpdateDisplay>().Single();
|
||||
transientUpdateDisplay.LatestUpdate.Value = new UserStatisticsUpdate(
|
||||
transientUpdateDisplay.LatestUpdate.Value = new ScoreBasedUserStatisticsUpdate(
|
||||
new ScoreInfo(),
|
||||
new UserStatistics
|
||||
{
|
||||
|
@ -170,7 +170,7 @@ public void TestTransientUserStatisticsDisplay()
|
|||
AddStep("Was null", () =>
|
||||
{
|
||||
var transientUpdateDisplay = this.ChildrenOfType<TransientUserStatisticsUpdateDisplay>().Single();
|
||||
transientUpdateDisplay.LatestUpdate.Value = new UserStatisticsUpdate(
|
||||
transientUpdateDisplay.LatestUpdate.Value = new ScoreBasedUserStatisticsUpdate(
|
||||
new ScoreInfo(),
|
||||
new UserStatistics
|
||||
{
|
||||
|
@ -187,7 +187,7 @@ public void TestTransientUserStatisticsDisplay()
|
|||
AddStep("Became null", () =>
|
||||
{
|
||||
var transientUpdateDisplay = this.ChildrenOfType<TransientUserStatisticsUpdateDisplay>().Single();
|
||||
transientUpdateDisplay.LatestUpdate.Value = new UserStatisticsUpdate(
|
||||
transientUpdateDisplay.LatestUpdate.Value = new ScoreBasedUserStatisticsUpdate(
|
||||
new ScoreInfo(),
|
||||
new UserStatistics
|
||||
{
|
||||
|
|
|
@ -3,14 +3,15 @@
|
|||
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Extensions.ObjectExtensions;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Online;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Catch;
|
||||
using osu.Game.Rulesets.Mania;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
|
@ -34,7 +35,7 @@ public void SetUpSteps()
|
|||
|
||||
AddStep("setup provider", () =>
|
||||
{
|
||||
OsuSpriteText text;
|
||||
OsuTextFlowContainer text;
|
||||
|
||||
((DummyAPIAccess)API).HandleRequest = r =>
|
||||
{
|
||||
|
@ -59,17 +60,31 @@ public void SetUpSteps()
|
|||
|
||||
Clear();
|
||||
Add(statisticsProvider = new LocalUserStatisticsProvider());
|
||||
Add(text = new OsuSpriteText
|
||||
Add(text = new OsuTextFlowContainer
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
});
|
||||
|
||||
statisticsProvider.Statistics.BindValueChanged(s =>
|
||||
statisticsProvider.StatisticsUpdate.BindValueChanged(s =>
|
||||
{
|
||||
text.Text = s.NewValue == null
|
||||
? "Statistics: (null)"
|
||||
: $"Statistics: (total score: {s.NewValue.TotalScore:N0})";
|
||||
text.Clear();
|
||||
|
||||
foreach (var ruleset in Dependencies.Get<RulesetStore>().AvailableRulesets)
|
||||
{
|
||||
text.AddText(statisticsProvider.GetStatisticsFor(ruleset) is UserStatistics statistics
|
||||
? $"{ruleset.Name} statistics: (total score: {statistics.TotalScore})"
|
||||
: $"{ruleset.Name} statistics: (null)");
|
||||
text.NewLine();
|
||||
}
|
||||
|
||||
if (s.NewValue == null)
|
||||
text.AddText("latest update: (null)");
|
||||
else
|
||||
{
|
||||
text.AddText($"latest update: {s.NewValue.Ruleset}"
|
||||
+ $" ({(s.NewValue.OldStatistics?.TotalScore.ToString() ?? "null")} -> {s.NewValue.NewStatistics.TotalScore})");
|
||||
}
|
||||
});
|
||||
|
||||
Ruleset.Value = new OsuRuleset().RulesetInfo;
|
||||
|
@ -79,19 +94,10 @@ public void SetUpSteps()
|
|||
[Test]
|
||||
public void TestInitialStatistics()
|
||||
{
|
||||
AddAssert("initial statistics populated", () => statisticsProvider.Statistics.Value.AsNonNull().TotalScore, () => Is.EqualTo(4_000_000));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestRulesetChanges()
|
||||
{
|
||||
AddAssert("statistics from osu", () => statisticsProvider.Statistics.Value.AsNonNull().TotalScore, () => Is.EqualTo(4_000_000));
|
||||
AddStep("change ruleset to taiko", () => Ruleset.Value = new TaikoRuleset().RulesetInfo);
|
||||
AddAssert("statistics from taiko", () => statisticsProvider.Statistics.Value.AsNonNull().TotalScore, () => Is.EqualTo(3_000_000));
|
||||
AddStep("change ruleset to catch", () => Ruleset.Value = new CatchRuleset().RulesetInfo);
|
||||
AddAssert("statistics from catch", () => statisticsProvider.Statistics.Value.AsNonNull().TotalScore, () => Is.EqualTo(2_000_000));
|
||||
AddStep("change ruleset to mania", () => Ruleset.Value = new ManiaRuleset().RulesetInfo);
|
||||
AddAssert("statistics from mania", () => statisticsProvider.Statistics.Value.AsNonNull().TotalScore, () => Is.EqualTo(1_000_000));
|
||||
AddAssert("osu statistics populated", () => statisticsProvider.GetStatisticsFor(new OsuRuleset().RulesetInfo)!.TotalScore, () => Is.EqualTo(4_000_000));
|
||||
AddAssert("taiko statistics populated", () => statisticsProvider.GetStatisticsFor(new TaikoRuleset().RulesetInfo)!.TotalScore, () => Is.EqualTo(3_000_000));
|
||||
AddAssert("catch statistics populated", () => statisticsProvider.GetStatisticsFor(new CatchRuleset().RulesetInfo)!.TotalScore, () => Is.EqualTo(2_000_000));
|
||||
AddAssert("mania statistics populated", () => statisticsProvider.GetStatisticsFor(new ManiaRuleset().RulesetInfo)!.TotalScore, () => Is.EqualTo(1_000_000));
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -105,18 +111,44 @@ public void TestUserChanges()
|
|||
serverSideStatistics[(1000, "taiko")] = new UserStatistics { TotalScore = 6_000_000 };
|
||||
});
|
||||
|
||||
AddAssert("statistics matches user 1001 from osu", () => statisticsProvider.Statistics.Value.AsNonNull().TotalScore, () => Is.EqualTo(4_000_000));
|
||||
AddAssert("statistics matches user 1001 in osu",
|
||||
() => statisticsProvider.GetStatisticsFor(new OsuRuleset().RulesetInfo)!.TotalScore,
|
||||
() => Is.EqualTo(4_000_000));
|
||||
|
||||
AddStep("change ruleset to taiko", () => Ruleset.Value = new TaikoRuleset().RulesetInfo);
|
||||
AddAssert("statistics matches user 1001 from taiko", () => statisticsProvider.Statistics.Value.AsNonNull().TotalScore, () => Is.EqualTo(3_000_000));
|
||||
AddAssert("statistics matches user 1001 in taiko",
|
||||
() => statisticsProvider.GetStatisticsFor(new TaikoRuleset().RulesetInfo)!.TotalScore,
|
||||
() => Is.EqualTo(3_000_000));
|
||||
|
||||
AddStep("change ruleset to osu", () => Ruleset.Value = new OsuRuleset().RulesetInfo);
|
||||
setUser(1000, false);
|
||||
|
||||
AddAssert("statistics matches user 1000 from osu", () => statisticsProvider.Statistics.Value.AsNonNull().TotalScore, () => Is.EqualTo(5_000_000));
|
||||
AddAssert("statistics matches user 1000 in osu",
|
||||
() => statisticsProvider.GetStatisticsFor(new OsuRuleset().RulesetInfo)!.TotalScore,
|
||||
() => Is.EqualTo(5_000_000));
|
||||
|
||||
AddStep("change ruleset to osu", () => Ruleset.Value = new TaikoRuleset().RulesetInfo);
|
||||
AddAssert("statistics matches user 1000 from taiko", () => statisticsProvider.Statistics.Value.AsNonNull().TotalScore, () => Is.EqualTo(6_000_000));
|
||||
AddAssert("statistics matches user 1000 in taiko",
|
||||
() => statisticsProvider.GetStatisticsFor(new TaikoRuleset().RulesetInfo)!.TotalScore,
|
||||
() => Is.EqualTo(6_000_000));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestRefetchStatistics()
|
||||
{
|
||||
setUser(1001);
|
||||
|
||||
AddStep("update statistics server side",
|
||||
() => serverSideStatistics[(1001, "osu")] = new UserStatistics { TotalScore = 9_000_000 });
|
||||
|
||||
AddAssert("statistics match old score",
|
||||
() => statisticsProvider.GetStatisticsFor(new OsuRuleset().RulesetInfo)!.TotalScore,
|
||||
() => Is.EqualTo(4_000_000));
|
||||
|
||||
AddStep("request refetch", () => statisticsProvider.RefetchStatistics(new OsuRuleset().RulesetInfo));
|
||||
AddUntilStep("statistics update raised",
|
||||
() => statisticsProvider.StatisticsUpdate.Value.NewStatistics.TotalScore,
|
||||
() => Is.EqualTo(9_000_000));
|
||||
AddAssert("statistics match new score",
|
||||
() => statisticsProvider.GetStatisticsFor(new OsuRuleset().RulesetInfo)!.TotalScore,
|
||||
() => Is.EqualTo(9_000_000));
|
||||
}
|
||||
|
||||
private UserStatistics tryGetStatistics(int userId, string rulesetName)
|
||||
|
|
|
@ -34,8 +34,8 @@ public partial class TestSceneUserPanel : OsuTestScene
|
|||
[Cached]
|
||||
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple);
|
||||
|
||||
[Cached]
|
||||
private readonly LocalUserStatisticsProvider statisticsProvider = new LocalUserStatisticsProvider();
|
||||
[Cached(typeof(LocalUserStatisticsProvider))]
|
||||
private readonly TestUserStatisticsProvider statisticsProvider = new TestUserStatisticsProvider();
|
||||
|
||||
[Resolved]
|
||||
private IRulesetStore rulesetStore { get; set; }
|
||||
|
@ -206,5 +206,11 @@ public TestUserListPanel(APIUser user)
|
|||
|
||||
public new TextFlowContainer LastVisitMessage => base.LastVisitMessage;
|
||||
}
|
||||
|
||||
private partial class TestUserStatisticsProvider : LocalUserStatisticsProvider
|
||||
{
|
||||
public new void UpdateStatistics(UserStatistics newStatistics, RulesetInfo ruleset)
|
||||
=> base.UpdateStatistics(newStatistics, ruleset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -126,7 +126,7 @@ public void TestStatisticsUpdateFiredAfterRegistrationAddedAndScoreProcessed()
|
|||
|
||||
var ruleset = new OsuRuleset().RulesetInfo;
|
||||
|
||||
UserStatisticsUpdate? update = null;
|
||||
ScoreBasedUserStatisticsUpdate? update = null;
|
||||
registerForUpdates(scoreId, ruleset, receivedUpdate => update = receivedUpdate);
|
||||
|
||||
feignScoreProcessing(userId, ruleset, 5_000_000);
|
||||
|
@ -149,7 +149,7 @@ public void TestStatisticsUpdateFiredAfterScoreProcessedAndRegistrationAdded()
|
|||
// note ordering - in this test processing completes *before* the registration is added.
|
||||
feignScoreProcessing(userId, ruleset, 5_000_000);
|
||||
|
||||
UserStatisticsUpdate? update = null;
|
||||
ScoreBasedUserStatisticsUpdate? update = null;
|
||||
registerForUpdates(scoreId, ruleset, receivedUpdate => update = receivedUpdate);
|
||||
|
||||
AddStep("signal score processed", () => ((ISpectatorClient)spectatorClient).UserScoreProcessed(userId, scoreId));
|
||||
|
@ -167,7 +167,7 @@ public void TestStatisticsUpdateNotFiredIfUserLoggedOut()
|
|||
long scoreId = getScoreId();
|
||||
var ruleset = new OsuRuleset().RulesetInfo;
|
||||
|
||||
UserStatisticsUpdate? update = null;
|
||||
ScoreBasedUserStatisticsUpdate? update = null;
|
||||
registerForUpdates(scoreId, ruleset, receivedUpdate => update = receivedUpdate);
|
||||
|
||||
feignScoreProcessing(userId, ruleset, 5_000_000);
|
||||
|
@ -194,7 +194,7 @@ public void TestStatisticsUpdateNotFiredIfAnotherUserLoggedIn()
|
|||
long scoreId = getScoreId();
|
||||
var ruleset = new OsuRuleset().RulesetInfo;
|
||||
|
||||
UserStatisticsUpdate? update = null;
|
||||
ScoreBasedUserStatisticsUpdate? update = null;
|
||||
registerForUpdates(scoreId, ruleset, receivedUpdate => update = receivedUpdate);
|
||||
|
||||
feignScoreProcessing(userId, ruleset, 5_000_000);
|
||||
|
@ -215,7 +215,7 @@ public void TestStatisticsUpdateNotFiredIfScoreIdDoesNotMatch()
|
|||
long scoreId = getScoreId();
|
||||
var ruleset = new OsuRuleset().RulesetInfo;
|
||||
|
||||
UserStatisticsUpdate? update = null;
|
||||
ScoreBasedUserStatisticsUpdate? update = null;
|
||||
registerForUpdates(scoreId, ruleset, receivedUpdate => update = receivedUpdate);
|
||||
|
||||
feignScoreProcessing(userId, ruleset, 5_000_000);
|
||||
|
@ -244,7 +244,7 @@ public void TestIgnoredScoreUpdateIsMergedIntoNextOne()
|
|||
|
||||
feignScoreProcessing(userId, ruleset, 6_000_000);
|
||||
|
||||
UserStatisticsUpdate? update = null;
|
||||
ScoreBasedUserStatisticsUpdate? update = null;
|
||||
registerForUpdates(secondScoreId, ruleset, receivedUpdate => update = receivedUpdate);
|
||||
|
||||
AddStep("signal score processed", () => ((ISpectatorClient)spectatorClient).UserScoreProcessed(userId, secondScoreId));
|
||||
|
@ -262,15 +262,14 @@ public void TestGlobalStatisticsUpdatedAfterRegistrationAddedAndScoreProcessed()
|
|||
|
||||
var ruleset = new OsuRuleset().RulesetInfo;
|
||||
|
||||
UserStatisticsUpdate? update = null;
|
||||
ScoreBasedUserStatisticsUpdate? 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", () => statisticsProvider.Statistics.Value!.TotalScore, () => Is.EqualTo(5_000_000));
|
||||
AddAssert("statistics values are correct", () => dummyAPI.LocalUser.Value.Statistics.TotalScore, () => Is.EqualTo(5_000_000));
|
||||
}
|
||||
|
||||
private int nextUserId = 2000;
|
||||
|
@ -292,7 +291,7 @@ private void setUpUser(int userId)
|
|||
});
|
||||
}
|
||||
|
||||
private void registerForUpdates(long scoreId, RulesetInfo rulesetInfo, Action<UserStatisticsUpdate> onUpdateReady) =>
|
||||
private void registerForUpdates(long scoreId, RulesetInfo rulesetInfo, Action<ScoreBasedUserStatisticsUpdate> onUpdateReady) =>
|
||||
AddStep("register for updates", () =>
|
||||
{
|
||||
watcher.RegisterForStatisticsUpdateAfter(
|
||||
|
|
|
@ -112,6 +112,6 @@ public void TestNotRanked()
|
|||
});
|
||||
|
||||
private void displayUpdate(UserStatistics before, UserStatistics after) =>
|
||||
AddStep("display update", () => overallRanking.StatisticsUpdate.Value = new UserStatisticsUpdate(new ScoreInfo(), before, after));
|
||||
AddStep("display update", () => overallRanking.StatisticsUpdate.Value = new ScoreBasedUserStatisticsUpdate(new ScoreInfo(), before, after));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -91,12 +91,12 @@ public void TestStatisticsShownCorrectlyIfUpdateDeliveredBeforeLoad()
|
|||
UserStatisticsWatcher userStatisticsWatcher = null!;
|
||||
ScoreInfo score = null!;
|
||||
|
||||
AddStep("create user statistics watcher", () => Add(userStatisticsWatcher = new UserStatisticsWatcher()));
|
||||
AddStep("create user statistics watcher", () => Add(userStatisticsWatcher = new UserStatisticsWatcher(new LocalUserStatisticsProvider())));
|
||||
AddStep("set user statistics update", () =>
|
||||
{
|
||||
score = TestResources.CreateTestScoreInfo();
|
||||
score.OnlineID = 1234;
|
||||
((Bindable<UserStatisticsUpdate>)userStatisticsWatcher.LatestUpdate).Value = new UserStatisticsUpdate(score,
|
||||
((Bindable<ScoreBasedUserStatisticsUpdate>)userStatisticsWatcher.LatestUpdate).Value = new ScoreBasedUserStatisticsUpdate(score,
|
||||
new UserStatistics
|
||||
{
|
||||
Level = new UserStatistics.LevelInfo
|
||||
|
@ -157,7 +157,7 @@ private void loadPanel(ScoreInfo score) => AddStep("load panel", () =>
|
|||
Score = { Value = score },
|
||||
DisplayedUserStatisticsUpdate =
|
||||
{
|
||||
Value = new UserStatisticsUpdate(score, new UserStatistics
|
||||
Value = new ScoreBasedUserStatisticsUpdate(score, new UserStatistics
|
||||
{
|
||||
Level = new UserStatistics.LevelInfo
|
||||
{
|
||||
|
|
|
@ -223,12 +223,14 @@ public class KudosuCount
|
|||
|
||||
/// <summary>
|
||||
/// User statistics for the requested ruleset (in the case of a <see cref="GetUserRequest"/> or <see cref="GetFriendsRequest"/> response).
|
||||
/// Otherwise empty.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This returns null when accessed from <see cref="IAPIProvider.LocalUser"/>. Use <see cref="LocalUserStatisticsProvider"/> instead.
|
||||
/// </remarks>
|
||||
[JsonProperty(@"statistics")]
|
||||
public UserStatistics Statistics
|
||||
{
|
||||
get => statistics ??= new UserStatistics();
|
||||
get => statistics;
|
||||
set
|
||||
{
|
||||
if (statistics != null)
|
||||
|
@ -242,7 +244,11 @@ public UserStatistics Statistics
|
|||
[JsonProperty(@"rank_history")]
|
||||
private APIRankHistory rankHistory
|
||||
{
|
||||
set => Statistics.RankHistory = value;
|
||||
set
|
||||
{
|
||||
statistics ??= new UserStatistics();
|
||||
statistics.RankHistory = value;
|
||||
}
|
||||
}
|
||||
|
||||
[JsonProperty(@"active_tournament_banners")]
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
|
@ -18,84 +19,63 @@ namespace osu.Game.Online
|
|||
/// </summary>
|
||||
public partial class LocalUserStatisticsProvider : Component
|
||||
{
|
||||
private readonly Bindable<UserStatisticsUpdate> statisticsUpdate = new Bindable<UserStatisticsUpdate>();
|
||||
|
||||
/// <summary>
|
||||
/// A bindable communicating updates to the local user's statistics on any ruleset.
|
||||
/// This does not guarantee the presence of old statistics, as it is invoked on initial population of statistics.
|
||||
/// </summary>
|
||||
public IBindable<UserStatisticsUpdate> StatisticsUpdate => statisticsUpdate;
|
||||
|
||||
[Resolved]
|
||||
private IBindable<RulesetInfo> ruleset { get; set; } = null!;
|
||||
private RulesetStore rulesets { get; set; } = null!;
|
||||
|
||||
[Resolved]
|
||||
private IAPIProvider api { get; set; } = null!;
|
||||
|
||||
private readonly Dictionary<string, UserStatistics> statisticsCache = new Dictionary<string, UserStatistics>();
|
||||
private readonly Dictionary<string, GetUserRequest> statisticsRequests = new Dictionary<string, GetUserRequest>();
|
||||
|
||||
/// <summary>
|
||||
/// The statistics of the local user for the game-wide selected ruleset.
|
||||
/// Returns the <see cref="UserStatistics"/> currently available for the given ruleset.
|
||||
/// This may return null if the requested statistics has not been fetched before yet.
|
||||
/// </summary>
|
||||
public IBindable<UserStatistics?> Statistics => statistics;
|
||||
|
||||
private readonly Bindable<UserStatistics?> statistics = new Bindable<UserStatistics?>();
|
||||
|
||||
private readonly Dictionary<string, UserStatistics> allStatistics = new Dictionary<string, UserStatistics>();
|
||||
/// <param name="ruleset">The ruleset to return the corresponding <see cref="UserStatistics"/> for.</param>
|
||||
public UserStatistics? GetStatisticsFor(RulesetInfo ruleset) => statisticsCache.GetValueOrDefault(ruleset.ShortName);
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
statistics.BindValueChanged(v =>
|
||||
{
|
||||
if (api.LocalUser.Value != null && v.NewValue != null)
|
||||
api.LocalUser.Value.Statistics = v.NewValue;
|
||||
});
|
||||
|
||||
ruleset.BindValueChanged(_ => updateStatisticsBindable());
|
||||
|
||||
api.LocalUser.BindValueChanged(_ =>
|
||||
{
|
||||
allStatistics.Clear();
|
||||
updateStatisticsBindable();
|
||||
}, true);
|
||||
api.LocalUser.BindValueChanged(_ => initialiseStatistics(), true);
|
||||
}
|
||||
|
||||
private GetUserRequest? currentRequest;
|
||||
|
||||
private void updateStatisticsBindable() => Schedule(() =>
|
||||
private void initialiseStatistics()
|
||||
{
|
||||
statistics.Value = null;
|
||||
statisticsCache.Clear();
|
||||
|
||||
if (api.LocalUser.Value == null || api.LocalUser.Value.OnlineID <= 1 || !ruleset.Value.IsLegacyRuleset())
|
||||
{
|
||||
statistics.Value = new UserStatistics();
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentRequest?.CompletionState == APIRequestCompletionState.Waiting)
|
||||
{
|
||||
currentRequest.Cancel();
|
||||
currentRequest = null;
|
||||
}
|
||||
|
||||
if (allStatistics.TryGetValue(ruleset.Value.ShortName, out var existing))
|
||||
statistics.Value = existing;
|
||||
else
|
||||
requestStatistics(ruleset.Value);
|
||||
});
|
||||
|
||||
private void requestStatistics(RulesetInfo ruleset)
|
||||
{
|
||||
currentRequest = new GetUserRequest(api.LocalUser.Value.OnlineID, ruleset);
|
||||
currentRequest.Success += u => statistics.Value = allStatistics[ruleset.ShortName] = u.Statistics;
|
||||
api.Queue(currentRequest);
|
||||
foreach (var ruleset in rulesets.AvailableRulesets.Where(r => r.IsLegacyRuleset()))
|
||||
RefetchStatistics(ruleset);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the <see cref="UserStatistics"/> currently available for the given ruleset.
|
||||
/// This may return null if the requested statistics has not been fetched yet.
|
||||
/// </summary>
|
||||
/// <param name="ruleset">The ruleset to return the corresponding <see cref="UserStatistics"/> for.</param>
|
||||
internal UserStatistics? GetStatisticsFor(RulesetInfo ruleset) => allStatistics.GetValueOrDefault(ruleset.ShortName);
|
||||
|
||||
internal void UpdateStatistics(UserStatistics statistics, RulesetInfo statisticsRuleset)
|
||||
public void RefetchStatistics(RulesetInfo ruleset)
|
||||
{
|
||||
allStatistics[statisticsRuleset.ShortName] = statistics;
|
||||
if (statisticsRequests.TryGetValue(ruleset.ShortName, out var previousRequest))
|
||||
previousRequest.Cancel();
|
||||
|
||||
if (statisticsRuleset.ShortName == ruleset.Value.ShortName)
|
||||
updateStatisticsBindable();
|
||||
var request = statisticsRequests[ruleset.ShortName] = new GetUserRequest(api.LocalUser.Value.Id, ruleset);
|
||||
request.Success += u => UpdateStatistics(u.Statistics, ruleset);
|
||||
api.Queue(request);
|
||||
}
|
||||
|
||||
protected void UpdateStatistics(UserStatistics newStatistics, RulesetInfo ruleset)
|
||||
{
|
||||
var oldStatistics = statisticsCache.GetValueOrDefault(ruleset.ShortName);
|
||||
|
||||
statisticsRequests.Remove(ruleset.ShortName);
|
||||
statisticsCache[ruleset.ShortName] = newStatistics;
|
||||
statisticsUpdate.Value = new UserStatisticsUpdate(ruleset, oldStatistics, newStatistics);
|
||||
}
|
||||
}
|
||||
|
||||
public record UserStatisticsUpdate(RulesetInfo Ruleset, UserStatistics? OldStatistics, UserStatistics NewStatistics);
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ namespace osu.Game.Online
|
|||
/// <summary>
|
||||
/// Contains data about the change in a user's profile statistics after completing a score.
|
||||
/// </summary>
|
||||
public class UserStatisticsUpdate
|
||||
public class ScoreBasedUserStatisticsUpdate
|
||||
{
|
||||
/// <summary>
|
||||
/// The score set by the user that triggered the update.
|
||||
|
@ -27,12 +27,12 @@ public class UserStatisticsUpdate
|
|||
public UserStatistics After { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="UserStatisticsUpdate"/>.
|
||||
/// Creates a new <see cref="ScoreBasedUserStatisticsUpdate"/>.
|
||||
/// </summary>
|
||||
/// <param name="score">The score set by the user that triggered the update.</param>
|
||||
/// <param name="before">The user's profile statistics prior to the score being set.</param>
|
||||
/// <param name="after">The user's profile statistics after the score was set.</param>
|
||||
public UserStatisticsUpdate(ScoreInfo score, UserStatistics before, UserStatistics after)
|
||||
public ScoreBasedUserStatisticsUpdate(ScoreInfo score, UserStatistics before, UserStatistics after)
|
||||
{
|
||||
Score = score;
|
||||
Before = before;
|
|
@ -8,10 +8,8 @@
|
|||
using osu.Framework.Graphics;
|
||||
using osu.Game.Extensions;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using osu.Game.Online.Spectator;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Online
|
||||
{
|
||||
|
@ -20,9 +18,12 @@ namespace osu.Game.Online
|
|||
/// </summary>
|
||||
public partial class UserStatisticsWatcher : Component
|
||||
{
|
||||
private readonly LocalUserStatisticsProvider? statisticsProvider;
|
||||
public IBindable<UserStatisticsUpdate?> LatestUpdate => latestUpdate;
|
||||
private readonly Bindable<UserStatisticsUpdate?> latestUpdate = new Bindable<UserStatisticsUpdate?>();
|
||||
private readonly LocalUserStatisticsProvider statisticsProvider;
|
||||
|
||||
public IBindable<ScoreBasedUserStatisticsUpdate?> LatestUpdate => latestUpdate;
|
||||
private readonly Bindable<ScoreBasedUserStatisticsUpdate?> latestUpdate = new Bindable<ScoreBasedUserStatisticsUpdate?>();
|
||||
|
||||
private ScoreInfo? scorePendingUpdate;
|
||||
|
||||
[Resolved]
|
||||
private SpectatorClient spectatorClient { get; set; } = null!;
|
||||
|
@ -32,7 +33,7 @@ public partial class UserStatisticsWatcher : Component
|
|||
|
||||
private readonly Dictionary<long, ScoreInfo> watchedScores = new Dictionary<long, ScoreInfo>();
|
||||
|
||||
public UserStatisticsWatcher(LocalUserStatisticsProvider? statisticsProvider = null)
|
||||
public UserStatisticsWatcher(LocalUserStatisticsProvider statisticsProvider)
|
||||
{
|
||||
this.statisticsProvider = statisticsProvider;
|
||||
}
|
||||
|
@ -40,7 +41,9 @@ public UserStatisticsWatcher(LocalUserStatisticsProvider? statisticsProvider = n
|
|||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
spectatorClient.OnUserScoreProcessed += userScoreProcessed;
|
||||
statisticsProvider.StatisticsUpdate.ValueChanged += onStatisticsUpdated;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -69,27 +72,20 @@ private void userScoreProcessed(int userId, long scoreId)
|
|||
if (!watchedScores.Remove(scoreId, out var scoreInfo))
|
||||
return;
|
||||
|
||||
requestStatisticsUpdate(userId, scoreInfo);
|
||||
scorePendingUpdate = scoreInfo;
|
||||
statisticsProvider.RefetchStatistics(scoreInfo.Ruleset);
|
||||
}
|
||||
|
||||
private void requestStatisticsUpdate(int userId, ScoreInfo scoreInfo)
|
||||
private void onStatisticsUpdated(ValueChangedEvent<UserStatisticsUpdate> update) => Schedule(() =>
|
||||
{
|
||||
var request = new GetUserRequest(userId, scoreInfo.Ruleset);
|
||||
request.Success += user => Schedule(() => dispatchStatisticsUpdate(scoreInfo, user.Statistics));
|
||||
api.Queue(request);
|
||||
}
|
||||
|
||||
private void dispatchStatisticsUpdate(ScoreInfo scoreInfo, UserStatistics updatedStatistics)
|
||||
{
|
||||
if (statisticsProvider == null)
|
||||
if (scorePendingUpdate == null || !update.NewValue.Ruleset.Equals(scorePendingUpdate.Ruleset))
|
||||
return;
|
||||
|
||||
var latestRulesetStatistics = statisticsProvider.GetStatisticsFor(scoreInfo.Ruleset);
|
||||
statisticsProvider.UpdateStatistics(updatedStatistics, scoreInfo.Ruleset);
|
||||
if (update.NewValue.OldStatistics != null)
|
||||
latestUpdate.Value = new ScoreBasedUserStatisticsUpdate(scorePendingUpdate, update.NewValue.OldStatistics, update.NewValue.NewStatistics);
|
||||
|
||||
if (latestRulesetStatistics != null)
|
||||
latestUpdate.Value = new UserStatisticsUpdate(scoreInfo, latestRulesetStatistics, updatedStatistics);
|
||||
}
|
||||
scorePendingUpdate = null;
|
||||
});
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
|
|
|
@ -21,7 +21,7 @@ namespace osu.Game.Overlays.Toolbar
|
|||
{
|
||||
public partial class TransientUserStatisticsUpdateDisplay : CompositeDrawable
|
||||
{
|
||||
public Bindable<UserStatisticsUpdate?> LatestUpdate { get; } = new Bindable<UserStatisticsUpdate?>();
|
||||
public Bindable<ScoreBasedUserStatisticsUpdate?> LatestUpdate { get; } = new Bindable<ScoreBasedUserStatisticsUpdate?>();
|
||||
|
||||
private Statistic<int> globalRank = null!;
|
||||
private Statistic<int> pp = null!;
|
||||
|
@ -48,7 +48,7 @@ private void load(UserStatisticsWatcher? userStatisticsWatcher)
|
|||
};
|
||||
|
||||
if (userStatisticsWatcher != null)
|
||||
((IBindable<UserStatisticsUpdate?>)LatestUpdate).BindTo(userStatisticsWatcher.LatestUpdate);
|
||||
((IBindable<ScoreBasedUserStatisticsUpdate?>)LatestUpdate).BindTo(userStatisticsWatcher.LatestUpdate);
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
|
|
|
@ -14,7 +14,7 @@ public partial class OverallRanking : CompositeDrawable
|
|||
{
|
||||
private const float transition_duration = 300;
|
||||
|
||||
public Bindable<UserStatisticsUpdate?> StatisticsUpdate { get; } = new Bindable<UserStatisticsUpdate?>();
|
||||
public Bindable<ScoreBasedUserStatisticsUpdate?> StatisticsUpdate { get; } = new Bindable<ScoreBasedUserStatisticsUpdate?>();
|
||||
|
||||
private LoadingLayer loadingLayer = null!;
|
||||
private GridContainer content = null!;
|
||||
|
@ -86,7 +86,7 @@ protected override void LoadComplete()
|
|||
FinishTransforms(true);
|
||||
}
|
||||
|
||||
private void onUpdateReceived(ValueChangedEvent<UserStatisticsUpdate?> update)
|
||||
private void onUpdateReceived(ValueChangedEvent<ScoreBasedUserStatisticsUpdate?> update)
|
||||
{
|
||||
if (update.NewValue == null)
|
||||
{
|
||||
|
|
|
@ -19,7 +19,7 @@ namespace osu.Game.Screens.Ranking.Statistics.User
|
|||
{
|
||||
public abstract partial class RankingChangeRow<T> : CompositeDrawable
|
||||
{
|
||||
public Bindable<UserStatisticsUpdate?> StatisticsUpdate { get; } = new Bindable<UserStatisticsUpdate?>();
|
||||
public Bindable<ScoreBasedUserStatisticsUpdate?> StatisticsUpdate { get; } = new Bindable<ScoreBasedUserStatisticsUpdate?>();
|
||||
|
||||
private readonly Func<UserStatistics, T> accessor;
|
||||
|
||||
|
@ -113,7 +113,7 @@ protected override void LoadComplete()
|
|||
StatisticsUpdate.BindValueChanged(onStatisticsUpdate, true);
|
||||
}
|
||||
|
||||
private void onStatisticsUpdate(ValueChangedEvent<UserStatisticsUpdate?> statisticsUpdate)
|
||||
private void onStatisticsUpdate(ValueChangedEvent<ScoreBasedUserStatisticsUpdate?> statisticsUpdate)
|
||||
{
|
||||
var update = statisticsUpdate.NewValue;
|
||||
|
||||
|
|
|
@ -18,9 +18,9 @@ public partial class UserStatisticsPanel : StatisticsPanel
|
|||
{
|
||||
private readonly ScoreInfo achievedScore;
|
||||
|
||||
internal readonly Bindable<UserStatisticsUpdate?> DisplayedUserStatisticsUpdate = new Bindable<UserStatisticsUpdate?>();
|
||||
internal readonly Bindable<ScoreBasedUserStatisticsUpdate?> DisplayedUserStatisticsUpdate = new Bindable<ScoreBasedUserStatisticsUpdate?>();
|
||||
|
||||
private IBindable<UserStatisticsUpdate?> latestGlobalStatisticsUpdate = null!;
|
||||
private IBindable<ScoreBasedUserStatisticsUpdate?> latestGlobalStatisticsUpdate = null!;
|
||||
|
||||
public UserStatisticsPanel(ScoreInfo achievedScore)
|
||||
{
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Overlays.Profile.Header.Components;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
using osu.Game.Rulesets;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Users
|
||||
|
@ -29,8 +30,6 @@ public partial class UserRankPanel : UserPanel
|
|||
private ProfileValueDisplay countryRankDisplay = null!;
|
||||
private LoadingLayer loadingLayer = null!;
|
||||
|
||||
private readonly IBindable<UserStatistics?> statistics = new Bindable<UserStatistics?>();
|
||||
|
||||
public UserRankPanel(APIUser user)
|
||||
: base(user)
|
||||
{
|
||||
|
@ -47,22 +46,31 @@ private void load()
|
|||
[Resolved]
|
||||
private LocalUserStatisticsProvider? statisticsProvider { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private IBindable<RulesetInfo> ruleset { get; set; } = null!;
|
||||
|
||||
private IBindable<UserStatisticsUpdate> statisticsUpdate = null!;
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
if (statisticsProvider != null)
|
||||
{
|
||||
statistics.BindTo(statisticsProvider.Statistics);
|
||||
statistics.BindValueChanged(stats =>
|
||||
{
|
||||
loadingLayer.State.Value = stats.NewValue == null ? Visibility.Visible : Visibility.Hidden;
|
||||
globalRankDisplay.Content = stats.NewValue?.GlobalRank?.ToLocalisableString("\\##,##0") ?? "-";
|
||||
countryRankDisplay.Content = stats.NewValue?.CountryRank?.ToLocalisableString("\\##,##0") ?? "-";
|
||||
}, true);
|
||||
statisticsUpdate = statisticsProvider.StatisticsUpdate.GetBoundCopy();
|
||||
statisticsUpdate.BindValueChanged(_ => updateDisplay(), true);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateDisplay()
|
||||
{
|
||||
var statistics = statisticsProvider?.GetStatisticsFor(ruleset.Value);
|
||||
|
||||
loadingLayer.State.Value = statistics == null ? Visibility.Visible : Visibility.Hidden;
|
||||
globalRankDisplay.Content = statistics?.GlobalRank?.ToLocalisableString("\\##,##0") ?? "-";
|
||||
countryRankDisplay.Content = statistics?.CountryRank?.ToLocalisableString("\\##,##0") ?? "-";
|
||||
}
|
||||
|
||||
protected override Drawable CreateLayout()
|
||||
{
|
||||
FillFlowContainer details;
|
||||
|
|
Loading…
Reference in New Issue