Merge pull request #11708 from frenzibyte/multiplayer-participant-rank

Display user global rank for multiplayer room participants
This commit is contained in:
Dean Herbert 2021-02-19 16:57:30 +09:00 committed by GitHub
commit b8c1e8ed7a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 91 additions and 55 deletions

View File

@ -105,7 +105,7 @@ namespace osu.Desktop
if (privacyMode.Value == DiscordRichPresenceMode.Limited) if (privacyMode.Value == DiscordRichPresenceMode.Limited)
presence.Assets.LargeImageText = string.Empty; presence.Assets.LargeImageText = string.Empty;
else else
presence.Assets.LargeImageText = $"{user.Value.Username}" + (user.Value.Statistics?.Ranks.Global > 0 ? $" (rank #{user.Value.Statistics.Ranks.Global:N0})" : string.Empty); presence.Assets.LargeImageText = $"{user.Value.Username}" + (user.Value.Statistics?.GlobalRank > 0 ? $" (rank #{user.Value.Statistics.GlobalRank:N0})" : string.Empty);
// update ruleset // update ruleset
presence.Assets.SmallImageKey = ruleset.Value.ID <= 3 ? $"mode_{ruleset.Value.ID}" : "mode_custom"; presence.Assets.SmallImageKey = ruleset.Value.ID <= 3 ? $"mode_{ruleset.Value.ID}" : "mode_custom";

View File

@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using System.Linq; using System.Linq;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Graphics; using osu.Framework.Graphics;
@ -155,7 +156,13 @@ namespace osu.Game.Tests.Visual.Multiplayer
{ {
Id = i, Id = i,
Username = $"User {i}", Username = $"User {i}",
CurrentModeRank = RNG.Next(1, 100000), RulesetsStatistics = new Dictionary<string, UserStatistics>
{
{
Ruleset.Value.ShortName,
new UserStatistics { GlobalRank = RNG.Next(1, 100000), }
}
},
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg", CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg",
}); });
@ -193,7 +200,13 @@ namespace osu.Game.Tests.Visual.Multiplayer
{ {
Id = 0, Id = 0,
Username = "User 0", Username = "User 0",
CurrentModeRank = RNG.Next(1, 100000), RulesetsStatistics = new Dictionary<string, UserStatistics>
{
{
Ruleset.Value.ShortName,
new UserStatistics { GlobalRank = RNG.Next(1, 100000), }
}
},
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg", CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg",
}); });

View File

@ -70,7 +70,7 @@ namespace osu.Game.Tests.Visual.Online
{ {
graph.Statistics.Value = new UserStatistics graph.Statistics.Value = new UserStatistics
{ {
Ranks = new UserStatistics.UserRanks { Global = 123456 }, GlobalRank = 123456,
PP = 12345, PP = 12345,
}; };
}); });
@ -79,7 +79,7 @@ namespace osu.Game.Tests.Visual.Online
{ {
graph.Statistics.Value = new UserStatistics graph.Statistics.Value = new UserStatistics
{ {
Ranks = new UserStatistics.UserRanks { Global = 89000 }, GlobalRank = 89000,
PP = 12345, PP = 12345,
RankHistory = new User.RankHistoryData RankHistory = new User.RankHistoryData
{ {
@ -92,7 +92,7 @@ namespace osu.Game.Tests.Visual.Online
{ {
graph.Statistics.Value = new UserStatistics graph.Statistics.Value = new UserStatistics
{ {
Ranks = new UserStatistics.UserRanks { Global = 89000 }, GlobalRank = 89000,
PP = 12345, PP = 12345,
RankHistory = new User.RankHistoryData RankHistory = new User.RankHistoryData
{ {
@ -105,7 +105,7 @@ namespace osu.Game.Tests.Visual.Online
{ {
graph.Statistics.Value = new UserStatistics graph.Statistics.Value = new UserStatistics
{ {
Ranks = new UserStatistics.UserRanks { Global = 12000 }, GlobalRank = 12000,
PP = 12345, PP = 12345,
RankHistory = new User.RankHistoryData RankHistory = new User.RankHistoryData
{ {
@ -118,7 +118,7 @@ namespace osu.Game.Tests.Visual.Online
{ {
graph.Statistics.Value = new UserStatistics graph.Statistics.Value = new UserStatistics
{ {
Ranks = new UserStatistics.UserRanks { Global = 12000 }, GlobalRank = 12000,
PP = 12345, PP = 12345,
RankHistory = new User.RankHistoryData RankHistory = new User.RankHistoryData
{ {

View File

@ -33,7 +33,8 @@ namespace osu.Game.Tests.Visual.Online
ProfileOrder = new[] { "me" }, ProfileOrder = new[] { "me" },
Statistics = new UserStatistics Statistics = new UserStatistics
{ {
Ranks = new UserStatistics.UserRanks { Global = 2148, Country = 1 }, GlobalRank = 2148,
CountryRank = 1,
PP = 4567.89m, PP = 4567.89m,
Level = new UserStatistics.LevelInfo Level = new UserStatistics.LevelInfo
{ {

View File

@ -113,11 +113,11 @@ namespace osu.Game.Tournament.Tests
}, },
Players = Players =
{ {
new User { Username = "Hello", Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 12 } } }, new User { Username = "Hello", Statistics = new UserStatistics { GlobalRank = 12 } },
new User { Username = "Hello", Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 16 } } }, new User { Username = "Hello", Statistics = new UserStatistics { GlobalRank = 16 } },
new User { Username = "Hello", Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 20 } } }, new User { Username = "Hello", Statistics = new UserStatistics { GlobalRank = 20 } },
new User { Username = "Hello", Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 24 } } }, new User { Username = "Hello", Statistics = new UserStatistics { GlobalRank = 24 } },
new User { Username = "Hello", Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 30 } } }, new User { Username = "Hello", Statistics = new UserStatistics { GlobalRank = 30 } },
} }
} }
}, },

View File

@ -36,7 +36,7 @@ namespace osu.Game.Tournament.Models
{ {
get get
{ {
var ranks = Players.Select(p => p.Statistics?.Ranks.Global) var ranks = Players.Select(p => p.Statistics?.GlobalRank)
.Where(i => i.HasValue) .Where(i => i.HasValue)
.Select(i => i.Value) .Select(i => i.Value)
.ToArray(); .ToArray();

View File

@ -250,7 +250,7 @@ namespace osu.Game.Tournament.Screens.TeamIntro
}; };
foreach (var p in team.Players) foreach (var p in team.Players)
fill.Add(new RowDisplay(p.Username, p.Statistics?.Ranks.Global?.ToString("\\##,0") ?? "-")); fill.Add(new RowDisplay(p.Username, p.Statistics?.GlobalRank?.ToString("\\##,0") ?? "-"));
} }
internal class RowDisplay : CompositeDrawable internal class RowDisplay : CompositeDrawable

View File

@ -150,7 +150,7 @@ namespace osu.Game.Tournament
{ {
foreach (var p in t.Players) foreach (var p in t.Players)
{ {
if (string.IsNullOrEmpty(p.Username) || p.Statistics == null) if (string.IsNullOrEmpty(p.Username) || p.Statistics?.GlobalRank == null)
{ {
PopulateUser(p, immediate: true); PopulateUser(p, immediate: true);
addedInfo = true; addedInfo = true;

View File

@ -144,8 +144,8 @@ namespace osu.Game.Overlays.Profile.Header
private void updateDisplay(User user) private void updateDisplay(User user)
{ {
hiddenDetailGlobal.Content = user?.Statistics?.Ranks.Global?.ToString("\\##,##0") ?? "-"; hiddenDetailGlobal.Content = user?.Statistics?.GlobalRank?.ToString("\\##,##0") ?? "-";
hiddenDetailCountry.Content = user?.Statistics?.Ranks.Country?.ToString("\\##,##0") ?? "-"; hiddenDetailCountry.Content = user?.Statistics?.CountryRank?.ToString("\\##,##0") ?? "-";
} }
} }
} }

View File

@ -176,8 +176,8 @@ namespace osu.Game.Overlays.Profile.Header
foreach (var scoreRankInfo in scoreRankInfos) foreach (var scoreRankInfo in scoreRankInfos)
scoreRankInfo.Value.RankCount = user?.Statistics?.GradesCount[scoreRankInfo.Key] ?? 0; scoreRankInfo.Value.RankCount = user?.Statistics?.GradesCount[scoreRankInfo.Key] ?? 0;
detailGlobalRank.Content = user?.Statistics?.Ranks.Global?.ToString("\\##,##0") ?? "-"; detailGlobalRank.Content = user?.Statistics?.GlobalRank?.ToString("\\##,##0") ?? "-";
detailCountryRank.Content = user?.Statistics?.Ranks.Country?.ToString("\\##,##0") ?? "-"; detailCountryRank.Content = user?.Statistics?.CountryRank?.ToString("\\##,##0") ?? "-";
rankGraph.Statistics.Value = user?.Statistics; rankGraph.Statistics.Value = user?.Statistics;
} }

View File

@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using System.Linq; using System.Linq;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.Color4Extensions;
@ -35,9 +36,10 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants
[Resolved] [Resolved]
private RulesetStore rulesets { get; set; } private RulesetStore rulesets { get; set; }
private SpriteIcon crown;
private OsuSpriteText userRankText;
private ModDisplay userModsDisplay; private ModDisplay userModsDisplay;
private StateDisplay userStateDisplay; private StateDisplay userStateDisplay;
private SpriteIcon crown;
public ParticipantPanel(MultiplayerRoomUser user) public ParticipantPanel(MultiplayerRoomUser user)
{ {
@ -119,12 +121,11 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants
Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 18), Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 18),
Text = user?.Username Text = user?.Username
}, },
new OsuSpriteText userRankText = new OsuSpriteText
{ {
Anchor = Anchor.CentreLeft, Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft, Origin = Anchor.CentreLeft,
Font = OsuFont.GetFont(size: 14), Font = OsuFont.GetFont(size: 14),
Text = user?.CurrentModeRank != null ? $"#{user.CurrentModeRank}" : string.Empty
} }
} }
}, },
@ -162,6 +163,11 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants
const double fade_time = 50; const double fade_time = 50;
var ruleset = rulesets.GetRuleset(Room.Settings.RulesetID).CreateInstance();
var currentModeRank = User.User?.RulesetsStatistics?.GetValueOrDefault(ruleset.ShortName)?.GlobalRank;
userRankText.Text = currentModeRank != null ? $"#{currentModeRank.Value:N0}" : string.Empty;
userStateDisplay.UpdateStatus(User.State, User.BeatmapAvailability); userStateDisplay.UpdateStatus(User.State, User.BeatmapAvailability);
if (Room.Host?.Equals(User) == true) if (Room.Host?.Equals(User) == true)
@ -171,11 +177,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants
// If the mods are updated at the end of the frame, the flow container will skip a reflow cycle: https://github.com/ppy/osu-framework/issues/4187 // If the mods are updated at the end of the frame, the flow container will skip a reflow cycle: https://github.com/ppy/osu-framework/issues/4187
// This looks particularly jarring here, so re-schedule the update to that start of our frame as a fix. // This looks particularly jarring here, so re-schedule the update to that start of our frame as a fix.
Schedule(() => Schedule(() => userModsDisplay.Current.Value = User.Mods.Select(m => m.ToMod(ruleset)).ToList());
{
var ruleset = rulesets.GetRuleset(Room.Settings.RulesetID).CreateInstance();
userModsDisplay.Current.Value = User.Mods.Select(m => m.ToMod(ruleset)).ToList();
});
} }
public MenuItem[] ContextMenuItems public MenuItem[] ContextMenuItems

View File

@ -2,10 +2,13 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System; using System;
using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Linq; using System.Linq;
using JetBrains.Annotations;
using Newtonsoft.Json; using Newtonsoft.Json;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Game.Online.API.Requests;
namespace osu.Game.Users namespace osu.Game.Users
{ {
@ -178,6 +181,10 @@ namespace osu.Game.Users
private UserStatistics statistics; private UserStatistics statistics;
/// <summary>
/// User statistics for the requested ruleset (in the case of a <see cref="GetUserRequest"/> response).
/// Otherwise empty.
/// </summary>
[JsonProperty(@"statistics")] [JsonProperty(@"statistics")]
public UserStatistics Statistics public UserStatistics Statistics
{ {
@ -228,14 +235,14 @@ namespace osu.Game.Users
[JsonProperty("replays_watched_counts")] [JsonProperty("replays_watched_counts")]
public UserHistoryCount[] ReplaysWatchedCounts; public UserHistoryCount[] ReplaysWatchedCounts;
public class UserHistoryCount /// <summary>
{ /// All user statistics per ruleset's short name (in the case of a <see cref="GetUsersRequest"/> response).
[JsonProperty("start_date")] /// Otherwise empty. Can be altered for testing purposes.
public DateTime Date; /// </summary>
// todo: this should likely be moved to a separate UserCompact class at some point.
[JsonProperty("count")] [JsonProperty("statistics_rulesets")]
public long Count; [CanBeNull]
} public Dictionary<string, UserStatistics> RulesetsStatistics { get; set; }
public override string ToString() => Username; public override string ToString() => Username;
@ -249,6 +256,14 @@ namespace osu.Game.Users
Id = 0 Id = 0
}; };
public bool Equals(User other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return Id == other.Id;
}
public enum PlayStyle public enum PlayStyle
{ {
[Description("Keyboard")] [Description("Keyboard")]
@ -264,12 +279,13 @@ namespace osu.Game.Users
Touch, Touch,
} }
public bool Equals(User other) public class UserHistoryCount
{ {
if (ReferenceEquals(null, other)) return false; [JsonProperty("start_date")]
if (ReferenceEquals(this, other)) return true; public DateTime Date;
return Id == other.Id; [JsonProperty("count")]
public long Count;
} }
} }
} }

View File

@ -26,17 +26,24 @@ namespace osu.Game.Users
public int Progress; public int Progress;
} }
[JsonProperty(@"pp")] [JsonProperty(@"global_rank")]
public decimal? PP; public int? GlobalRank;
[JsonProperty(@"pp_rank")] // the API sometimes only returns this value in condensed user responses public int? CountryRank;
private int? rank
{
set => Ranks.Global = value;
}
[JsonProperty(@"rank")] [JsonProperty(@"rank")]
public UserRanks Ranks; private UserRanks ranks
{
// eventually that will also become an own json property instead of reading from a `rank` object.
// see https://github.com/ppy/osu-web/blob/cb79bb72186c8f1a25f6a6f5ef315123decb4231/app/Transformers/UserStatisticsTransformer.php#L53.
set => CountryRank = value.Country;
}
// populated via User model, as that's where the data currently lives.
public RankHistoryData RankHistory;
[JsonProperty(@"pp")]
public decimal? PP;
[JsonProperty(@"ranked_score")] [JsonProperty(@"ranked_score")]
public long RankedScore; public long RankedScore;
@ -113,15 +120,12 @@ namespace osu.Game.Users
} }
} }
public struct UserRanks #pragma warning disable 649
private struct UserRanks
{ {
[JsonProperty(@"global")]
public int? Global;
[JsonProperty(@"country")] [JsonProperty(@"country")]
public int? Country; public int? Country;
} }
#pragma warning restore 649
public RankHistoryData RankHistory;
} }
} }