From c3aa9d6f8a495f4ef592767ddab579f8c232ce5b Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 25 Dec 2024 23:30:24 +0900 Subject: [PATCH] Display user style in participant panel --- .../TestSceneMultiplayerParticipantsList.cs | 27 +++++ .../Participants/ParticipantPanel.cs | 105 +++++++++++++++++- 2 files changed, 128 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerParticipantsList.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerParticipantsList.cs index d88741ec0c..238a716f91 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerParticipantsList.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerParticipantsList.cs @@ -308,6 +308,33 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("set state: locally available", () => MultiplayerClient.ChangeUserBeatmapAvailability(0, BeatmapAvailability.LocallyAvailable())); } + [Test] + public void TestUserWithStyle() + { + AddStep("add users", () => + { + MultiplayerClient.AddUser(new APIUser + { + Id = 0, + Username = "User 0", + RulesetsStatistics = new Dictionary + { + { + Ruleset.Value.ShortName, + new UserStatistics { GlobalRank = RNG.Next(1, 100000), } + } + }, + CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg", + }); + + MultiplayerClient.ChangeUserStyle(0, 259, 2); + }); + + AddStep("set beatmap locally available", () => MultiplayerClient.ChangeUserBeatmapAvailability(0, BeatmapAvailability.LocallyAvailable())); + AddStep("change user style to beatmap: 258, ruleset: 1", () => MultiplayerClient.ChangeUserStyle(0, 258, 1)); + AddStep("change user style to beatmap: null, ruleset: null", () => MultiplayerClient.ChangeUserStyle(0, null, null)); + } + [Test] public void TestModOverlap() { diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs index 7e42b18240..64c4648125 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs @@ -4,6 +4,8 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading; +using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.ObjectExtensions; @@ -14,6 +16,9 @@ using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; +using osu.Framework.Logging; +using osu.Game.Beatmaps.Drawables; +using osu.Game.Database; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; @@ -47,6 +52,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants private SpriteIcon crown = null!; private OsuSpriteText userRankText = null!; + private StyleDisplayIcon userStyleDisplay = null!; private ModDisplay userModsDisplay = null!; private StateDisplay userStateDisplay = null!; @@ -149,16 +155,20 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants } } }, - new Container + new FillFlowContainer { Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, AutoSizeAxes = Axes.Both, Margin = new MarginPadding { Right = 70 }, - Child = userModsDisplay = new ModDisplay + Children = new Drawable[] { - Scale = new Vector2(0.5f), - ExpansionMode = ExpansionMode.AlwaysContracted, + userStyleDisplay = new StyleDisplayIcon(), + userModsDisplay = new ModDisplay + { + Scale = new Vector2(0.5f), + ExpansionMode = ExpansionMode.AlwaysContracted, + } } }, userStateDisplay = new StateDisplay @@ -208,9 +218,20 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants userStateDisplay.UpdateStatus(User.State, User.BeatmapAvailability); if ((User.BeatmapAvailability.State == DownloadState.LocallyAvailable) && (User.State != MultiplayerUserState.Spectating)) + { userModsDisplay.FadeIn(fade_time); + userStyleDisplay.FadeIn(fade_time); + } else + { userModsDisplay.FadeOut(fade_time); + userStyleDisplay.FadeOut(fade_time); + } + + if (User.BeatmapId == null && User.RulesetId == null) + userStyleDisplay.Style = null; + else + userStyleDisplay.Style = (User.BeatmapId ?? currentItem?.BeatmapID ?? 0, User.RulesetId ?? currentItem?.RulesetID ?? 0); kickButton.Alpha = client.IsHost && !User.Equals(client.LocalUser) ? 1 : 0; crown.Alpha = client.Room.Host?.Equals(User) == true ? 1 : 0; @@ -284,5 +305,81 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants IconHoverColour = colours.Red; } } + + private partial class StyleDisplayIcon : CompositeComponent + { + [Resolved] + private BeatmapLookupCache beatmapLookupCache { get; set; } = null!; + + [Resolved] + private RulesetStore rulesets { get; set; } = null!; + + public StyleDisplayIcon() + { + AutoSizeAxes = Axes.Both; + } + + private (int beatmap, int ruleset)? style; + + public (int beatmap, int ruleset)? Style + { + get => style; + set + { + if (style == value) + return; + + style = value; + Scheduler.Add(refresh); + } + } + + private CancellationTokenSource? cancellationSource; + + private void refresh() + { + cancellationSource?.Cancel(); + cancellationSource?.Dispose(); + cancellationSource = null; + + if (Style == null) + { + ClearInternal(); + return; + } + + cancellationSource = new CancellationTokenSource(); + CancellationToken token = cancellationSource.Token; + + int localBeatmap = Style.Value.beatmap; + int localRuleset = Style.Value.ruleset; + + Task.Run(async () => + { + try + { + var beatmap = await beatmapLookupCache.GetBeatmapAsync(localBeatmap, token).ConfigureAwait(false); + if (beatmap == null) + return; + + Schedule(() => + { + if (token.IsCancellationRequested) + return; + + InternalChild = new DifficultyIcon(beatmap, rulesets.GetRuleset(localRuleset)) + { + Size = new Vector2(20), + TooltipType = DifficultyIconTooltipType.Extended, + }; + }); + } + catch (Exception e) + { + Logger.Log($"Error while populating participant style icon {e}"); + } + }, token); + } + } } }