From ce9dd0c9203a160b20bd4b90fe2c87f078e55e58 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 28 Oct 2020 17:19:01 +0900 Subject: [PATCH 01/24] Fix enum descriptions not being displayed in OverlayHeaderTabControl --- osu.Game/Overlays/TabControlOverlayHeader.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/TabControlOverlayHeader.cs b/osu.Game/Overlays/TabControlOverlayHeader.cs index 61605d9e9e..7798dfa576 100644 --- a/osu.Game/Overlays/TabControlOverlayHeader.cs +++ b/osu.Game/Overlays/TabControlOverlayHeader.cs @@ -1,9 +1,11 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -104,7 +106,7 @@ namespace osu.Game.Overlays public OverlayHeaderTabItem(T value) : base(value) { - Text.Text = value.ToString().ToLower(); + Text.Text = ((Value as Enum)?.GetDescription() ?? Value.ToString()).ToLower(); Text.Font = OsuFont.GetFont(size: 14); Text.Margin = new MarginPadding { Vertical = 16.5f }; // 15px padding + 1.5px line-height difference compensation Bar.Margin = new MarginPadding { Bottom = bar_height }; From 32becb6882bf7a800565499cc70a38019bb563cd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 28 Oct 2020 17:19:14 +0900 Subject: [PATCH 02/24] Add simple listing of currently playing users --- .../Dashboard/CurrentlyPlayingDisplay.cs | 95 +++++++++++++++++++ .../Dashboard/DashboardOverlayHeader.cs | 7 +- osu.Game/Overlays/DashboardOverlay.cs | 4 + 3 files changed, 105 insertions(+), 1 deletion(-) create mode 100644 osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs diff --git a/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs b/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs new file mode 100644 index 0000000000..8a98614fa0 --- /dev/null +++ b/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs @@ -0,0 +1,95 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Specialized; +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Screens; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; +using osu.Game.Online.Spectator; +using osu.Game.Screens.Play; +using osu.Game.Users; +using osuTK; + +namespace osu.Game.Overlays.Dashboard +{ + internal class CurrentlyPlayingDisplay : CompositeDrawable + { + private IBindableList playingUsers; + + private FillFlowContainer userFlow; + + [Resolved] + private SpectatorStreamingClient spectatorStreaming { get; set; } + + [BackgroundDependencyLoader] + private void load() + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + + InternalChild = userFlow = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Spacing = new Vector2(10), + }; + } + + [Resolved] + private IAPIProvider api { get; set; } + + protected override void LoadComplete() + { + base.LoadComplete(); + + playingUsers = spectatorStreaming.PlayingUsers.GetBoundCopy(); + playingUsers.BindCollectionChanged((sender, e) => Schedule(() => + { + switch (e.Action) + { + case NotifyCollectionChangedAction.Add: + foreach (var u in e.NewItems.OfType()) + { + var request = new GetUserRequest(u); + request.Success += user => Schedule(() => + { + if (playingUsers.Contains((int)user.Id)) + userFlow.Add(createUserPanel(user)); + }); + api.Queue(request); + } + + break; + + case NotifyCollectionChangedAction.Remove: + foreach (var u in e.OldItems.OfType()) + userFlow.FirstOrDefault(card => card.User.Id == u)?.Expire(); + break; + + case NotifyCollectionChangedAction.Reset: + userFlow.Clear(); + break; + } + }), true); + } + + [Resolved] + private OsuGame game { get; set; } + + private UserPanel createUserPanel(User user) + { + return new UserGridPanel(user).With(panel => + { + panel.Anchor = Anchor.TopCentre; + panel.Origin = Anchor.TopCentre; + panel.Width = 290; + panel.Action = () => game.PerformFromScreen(s => s.Push(new Spectator(user))); + }); + } + } +} diff --git a/osu.Game/Overlays/Dashboard/DashboardOverlayHeader.cs b/osu.Game/Overlays/Dashboard/DashboardOverlayHeader.cs index 36bf589877..3314ed957a 100644 --- a/osu.Game/Overlays/Dashboard/DashboardOverlayHeader.cs +++ b/osu.Game/Overlays/Dashboard/DashboardOverlayHeader.cs @@ -1,6 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.ComponentModel; + namespace osu.Game.Overlays.Dashboard { public class DashboardOverlayHeader : TabControlOverlayHeader @@ -20,6 +22,9 @@ namespace osu.Game.Overlays.Dashboard public enum DashboardOverlayTabs { - Friends + Friends, + + [Description("Currently Playing")] + CurrentlyPlaying } } diff --git a/osu.Game/Overlays/DashboardOverlay.cs b/osu.Game/Overlays/DashboardOverlay.cs index a2490365e4..04defce636 100644 --- a/osu.Game/Overlays/DashboardOverlay.cs +++ b/osu.Game/Overlays/DashboardOverlay.cs @@ -130,6 +130,10 @@ namespace osu.Game.Overlays loadDisplay(new FriendDisplay()); break; + case DashboardOverlayTabs.CurrentlyPlaying: + loadDisplay(new CurrentlyPlayingDisplay()); + break; + default: throw new NotImplementedException($"Display for {tab.NewValue} tab is not implemented"); } From 84d854e23136434dcb5213abdf028f4c6ef63e6c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 28 Oct 2020 17:23:38 +0900 Subject: [PATCH 03/24] Avoid having the user profile show when clicking a spectator panel --- osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs | 1 + osu.Game/Users/UserPanel.cs | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs b/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs index 8a98614fa0..d71e582c05 100644 --- a/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs +++ b/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs @@ -88,6 +88,7 @@ namespace osu.Game.Overlays.Dashboard panel.Anchor = Anchor.TopCentre; panel.Origin = Anchor.TopCentre; panel.Width = 290; + panel.ShowProfileOnClick = false; panel.Action = () => game.PerformFromScreen(s => s.Push(new Spectator(user))); }); } diff --git a/osu.Game/Users/UserPanel.cs b/osu.Game/Users/UserPanel.cs index 57a87a713d..e97ff4287f 100644 --- a/osu.Game/Users/UserPanel.cs +++ b/osu.Game/Users/UserPanel.cs @@ -22,6 +22,8 @@ namespace osu.Game.Users public new Action Action; + public bool ShowProfileOnClick = true; + protected Action ViewProfile { get; private set; } protected Drawable Background { get; private set; } @@ -68,7 +70,8 @@ namespace osu.Game.Users base.Action = ViewProfile = () => { Action?.Invoke(); - profileOverlay?.ShowUser(User); + if (ShowProfileOnClick) + profileOverlay?.ShowUser(User); }; } From 16b0a7b33e2da40c759142fcfb29097c121e4439 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 28 Oct 2020 18:01:55 +0900 Subject: [PATCH 04/24] Add button flow to allow resuming watching after exiting manually --- osu.Game/Screens/Play/Spectator.cs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/Spectator.cs b/osu.Game/Screens/Play/Spectator.cs index 51cd5b59aa..11cdb66087 100644 --- a/osu.Game/Screens/Play/Spectator.cs +++ b/osu.Game/Screens/Play/Spectator.cs @@ -13,6 +13,7 @@ using osu.Framework.Screens; using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Online.Spectator; @@ -63,6 +64,8 @@ namespace osu.Game.Screens.Play private IBindable> managerUpdated; + private TriangleButton watchButton; + public Spectator([NotNull] User targetUser) { this.targetUser = targetUser ?? throw new ArgumentNullException(nameof(targetUser)); @@ -108,6 +111,14 @@ namespace osu.Game.Screens.Play Anchor = Anchor.Centre, Origin = Anchor.Centre, }, + watchButton = new TriangleButton + { + Text = "Watch", + Width = 250, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Action = attemptStart + } } }, }; @@ -191,9 +202,11 @@ namespace osu.Game.Screens.Play var resolvedBeatmap = beatmaps.QueryBeatmap(b => b.OnlineBeatmapID == state.BeatmapID); + showBeatmapPanel(state.BeatmapID.Value); + if (resolvedBeatmap == null) { - showBeatmapPanel(state.BeatmapID.Value); + watchButton.Enabled.Value = false; return; } @@ -209,6 +222,7 @@ namespace osu.Game.Screens.Play rulesetInstance = resolvedRuleset; beatmap.Value = beatmaps.GetWorkingBeatmap(resolvedBeatmap); + watchButton.Enabled.Value = true; this.Push(new SpectatorPlayerLoader(new Score { From c97feb09bf2872a564d452f89442ed9cd2eaafc4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 28 Oct 2020 18:05:15 +0900 Subject: [PATCH 05/24] Allow continuing to automatically spectate user from results screen --- osu.Game/Screens/Play/SpectatorPlayer.cs | 20 +++++++-- .../Screens/Play/SpectatorResultsScreen.cs | 42 +++++++++++++++++++ 2 files changed, 59 insertions(+), 3 deletions(-) create mode 100644 osu.Game/Screens/Play/SpectatorResultsScreen.cs diff --git a/osu.Game/Screens/Play/SpectatorPlayer.cs b/osu.Game/Screens/Play/SpectatorPlayer.cs index fbd21b32ba..93e755f30a 100644 --- a/osu.Game/Screens/Play/SpectatorPlayer.cs +++ b/osu.Game/Screens/Play/SpectatorPlayer.cs @@ -7,19 +7,25 @@ using osu.Framework.Screens; using osu.Game.Beatmaps; using osu.Game.Online.Spectator; using osu.Game.Scoring; +using osu.Game.Screens.Ranking; namespace osu.Game.Screens.Play { public class SpectatorPlayer : ReplayPlayer { - [Resolved] - private SpectatorStreamingClient spectatorStreaming { get; set; } - public SpectatorPlayer(Score score) : base(score) { } + protected override ResultsScreen CreateResults(ScoreInfo score) + { + return new SpectatorResultsScreen(score); + } + + [Resolved] + private SpectatorStreamingClient spectatorStreaming { get; set; } + [BackgroundDependencyLoader] private void load() { @@ -32,6 +38,14 @@ namespace osu.Game.Screens.Play Schedule(this.Exit); } + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (spectatorStreaming != null) + spectatorStreaming.OnUserBeganPlaying -= userBeganPlaying; + } + protected override GameplayClockContainer CreateGameplayClockContainer(WorkingBeatmap beatmap, double gameplayStart) { // if we already have frames, start gameplay at the point in time they exist, should they be too far into the beatmap. diff --git a/osu.Game/Screens/Play/SpectatorResultsScreen.cs b/osu.Game/Screens/Play/SpectatorResultsScreen.cs new file mode 100644 index 0000000000..bb1bdee3a9 --- /dev/null +++ b/osu.Game/Screens/Play/SpectatorResultsScreen.cs @@ -0,0 +1,42 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Screens; +using osu.Game.Online.Spectator; +using osu.Game.Scoring; +using osu.Game.Screens.Ranking; + +namespace osu.Game.Screens.Play +{ + public class SpectatorResultsScreen : SoloResultsScreen + { + public SpectatorResultsScreen(ScoreInfo score) + : base(score) + { + } + + [Resolved] + private SpectatorStreamingClient spectatorStreaming { get; set; } + + [BackgroundDependencyLoader] + private void load() + { + spectatorStreaming.OnUserBeganPlaying += userBeganPlaying; + } + + private void userBeganPlaying(int userId, SpectatorState state) + { + if (userId == Score.UserID) + Schedule(this.Exit); + } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (spectatorStreaming != null) + spectatorStreaming.OnUserBeganPlaying -= userBeganPlaying; + } + } +} From 2d73dfbe39067052d37310bc7031ad0f3d8681e1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 28 Oct 2020 18:16:19 +0900 Subject: [PATCH 06/24] Add more safety around beatmap panel and button display logic --- osu.Game/Screens/Play/Spectator.cs | 32 ++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/osu.Game/Screens/Play/Spectator.cs b/osu.Game/Screens/Play/Spectator.cs index 11cdb66087..d97883069e 100644 --- a/osu.Game/Screens/Play/Spectator.cs +++ b/osu.Game/Screens/Play/Spectator.cs @@ -183,14 +183,22 @@ namespace osu.Game.Screens.Play if (userId != targetUser.Id) return; - if (replay == null) return; + if (replay != null) + { + replay.HasReceivedAllFrames = true; + replay = null; + } - replay.HasReceivedAllFrames = true; - replay = null; + // not really going to start anything, but will clear the beatmap card out. + attemptStart(); } private void attemptStart() { + watchButton.Enabled.Value = false; + + showBeatmapPanel(state); + var resolvedRuleset = rulesets.AvailableRulesets.FirstOrDefault(r => r.ID == state.RulesetID)?.CreateInstance(); // ruleset not available @@ -202,13 +210,8 @@ namespace osu.Game.Screens.Play var resolvedBeatmap = beatmaps.QueryBeatmap(b => b.OnlineBeatmapID == state.BeatmapID); - showBeatmapPanel(state.BeatmapID.Value); - if (resolvedBeatmap == null) - { - watchButton.Enabled.Value = false; return; - } var scoreInfo = new ScoreInfo { @@ -231,11 +234,20 @@ namespace osu.Game.Screens.Play })); } - private void showBeatmapPanel(int beatmapId) + private void showBeatmapPanel(SpectatorState state) { - var req = new GetBeatmapSetRequest(beatmapId, BeatmapSetLookupType.BeatmapId); + if (state.BeatmapID == null) + { + beatmapPanelContainer.Clear(); + return; + } + + var req = new GetBeatmapSetRequest(state.BeatmapID.Value, BeatmapSetLookupType.BeatmapId); req.Success += res => Schedule(() => { + if (state != this.state) + return; + beatmapPanelContainer.Child = new GridBeatmapPanel(res.ToBeatmapSet(rulesets)); }); From 344ff8f4bc9bf76c98abe6c690917ce39d2ef55e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 28 Oct 2020 18:35:20 +0900 Subject: [PATCH 07/24] "Improve" visuals of spectator screen --- osu.Game/Screens/Play/Spectator.cs | 134 ++++++++++++++++++----------- 1 file changed, 86 insertions(+), 48 deletions(-) diff --git a/osu.Game/Screens/Play/Spectator.cs b/osu.Game/Screens/Play/Spectator.cs index d97883069e..19409ade93 100644 --- a/osu.Game/Screens/Play/Spectator.cs +++ b/osu.Game/Screens/Play/Spectator.cs @@ -9,6 +9,8 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; using osu.Framework.Screens; using osu.Game.Beatmaps; using osu.Game.Graphics; @@ -24,6 +26,7 @@ using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.Replays.Types; using osu.Game.Scoring; +using osu.Game.Screens.Multi.Match.Components; using osu.Game.Users; using osuTK; @@ -72,55 +75,83 @@ namespace osu.Game.Screens.Play } [BackgroundDependencyLoader] - private void load() + private void load(OsuColour colours) { - InternalChildren = new Drawable[] + InternalChild = new Container { - new FillFlowContainer + Masking = true, + CornerRadius = 20, + AutoSizeAxes = Axes.Both, + AutoSizeDuration = 500, + AutoSizeEasing = Easing.OutQuint, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Children = new Drawable[] { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Vertical, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Spacing = new Vector2(15), - Children = new Drawable[] + new Box { - new OsuSpriteText + Colour = colours.GreySeafoamDark, + RelativeSizeAxes = Axes.Both, + }, + new FillFlowContainer + { + Margin = new MarginPadding(20), + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Spacing = new Vector2(15), + Children = new Drawable[] { - Text = "Currently spectating", - Font = OsuFont.Default.With(size: 30), - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - }, - new UserGridPanel(targetUser) - { - Width = 290, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - }, - new OsuSpriteText - { - Text = "playing", - Font = OsuFont.Default.With(size: 30), - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - }, - beatmapPanelContainer = new Container - { - AutoSizeAxes = Axes.Both, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - }, - watchButton = new TriangleButton - { - Text = "Watch", - Width = 250, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Action = attemptStart + new OsuSpriteText + { + Text = "Spectator Mode", + Font = OsuFont.Default.With(size: 30), + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }, + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Spacing = new Vector2(15), + Children = new Drawable[] + { + new UserGridPanel(targetUser) + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Height = 145, + Width = 290, + }, + new SpriteIcon + { + Size = new Vector2(40), + Icon = FontAwesome.Solid.ArrowRight, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + }, + beatmapPanelContainer = new Container + { + AutoSizeAxes = Axes.Both, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + }, + } + }, + watchButton = new PurpleTriangleButton + { + Text = "Start Watching", + Width = 250, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Action = attemptStart + } } } - }, + } }; } @@ -141,7 +172,7 @@ namespace osu.Game.Screens.Play private void beatmapUpdated(ValueChangedEvent> beatmap) { if (beatmap.NewValue.TryGetTarget(out var beatmapSet) && beatmapSet.Beatmaps.Any(b => b.OnlineBeatmapID == state.BeatmapID)) - attemptStart(); + Schedule(attemptStart); } private void userSentFrames(int userId, FrameDataBundle data) @@ -189,14 +220,18 @@ namespace osu.Game.Screens.Play replay = null; } - // not really going to start anything, but will clear the beatmap card out. - attemptStart(); + clearDisplay(); + } + + private void clearDisplay() + { + watchButton.Enabled.Value = false; + beatmapPanelContainer.Clear(); } private void attemptStart() { - watchButton.Enabled.Value = false; - + clearDisplay(); showBeatmapPanel(state); var resolvedRuleset = rulesets.AvailableRulesets.FirstOrDefault(r => r.ID == state.RulesetID)?.CreateInstance(); @@ -213,6 +248,9 @@ namespace osu.Game.Screens.Play if (resolvedBeatmap == null) return; + if (replay == null) + return; + var scoreInfo = new ScoreInfo { Beatmap = resolvedBeatmap, @@ -236,7 +274,7 @@ namespace osu.Game.Screens.Play private void showBeatmapPanel(SpectatorState state) { - if (state.BeatmapID == null) + if (state?.BeatmapID == null) { beatmapPanelContainer.Clear(); return; From 93e3e1a4dbcc053f6c6a1f7d319a0a99aca0fc46 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 28 Oct 2020 18:40:06 +0900 Subject: [PATCH 08/24] Don't inherit ReplayPlayer to make results screen work correctly --- osu.Game/Screens/Play/SpectatorPlayer.cs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Play/SpectatorPlayer.cs b/osu.Game/Screens/Play/SpectatorPlayer.cs index 93e755f30a..bd05665ae7 100644 --- a/osu.Game/Screens/Play/SpectatorPlayer.cs +++ b/osu.Game/Screens/Play/SpectatorPlayer.cs @@ -11,11 +11,16 @@ using osu.Game.Screens.Ranking; namespace osu.Game.Screens.Play { - public class SpectatorPlayer : ReplayPlayer + public class SpectatorPlayer : Player { + private readonly Score score; + + protected override bool CheckModsAllowFailure() => false; // todo: better support starting mid-way through beatmap + public SpectatorPlayer(Score score) - : base(score) + : base(true, true) { + this.score = score; } protected override ResultsScreen CreateResults(ScoreInfo score) @@ -32,9 +37,14 @@ namespace osu.Game.Screens.Play spectatorStreaming.OnUserBeganPlaying += userBeganPlaying; } + protected override void PrepareReplay() + { + DrawableRuleset?.SetReplayScore(score); + } + private void userBeganPlaying(int userId, SpectatorState state) { - if (userId == Score.ScoreInfo.UserID) + if (userId == score.ScoreInfo.UserID) Schedule(this.Exit); } @@ -49,7 +59,7 @@ namespace osu.Game.Screens.Play protected override GameplayClockContainer CreateGameplayClockContainer(WorkingBeatmap beatmap, double gameplayStart) { // if we already have frames, start gameplay at the point in time they exist, should they be too far into the beatmap. - double? firstFrameTime = Score.Replay.Frames.FirstOrDefault()?.Time; + double? firstFrameTime = score.Replay.Frames.FirstOrDefault()?.Time; if (firstFrameTime == null || firstFrameTime <= gameplayStart + 5000) return base.CreateGameplayClockContainer(beatmap, gameplayStart); From 4df811985214df8702d772ecb1590eab0947c7fd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 28 Oct 2020 18:45:09 +0900 Subject: [PATCH 09/24] Add missing schedule --- osu.Game/Screens/Play/Spectator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/Spectator.cs b/osu.Game/Screens/Play/Spectator.cs index 19409ade93..9b26bd5ba3 100644 --- a/osu.Game/Screens/Play/Spectator.cs +++ b/osu.Game/Screens/Play/Spectator.cs @@ -220,7 +220,7 @@ namespace osu.Game.Screens.Play replay = null; } - clearDisplay(); + Schedule(clearDisplay); } private void clearDisplay() From 98070898348848f6e8e7d23bc7de5373fdc46a26 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 28 Oct 2020 18:47:15 +0900 Subject: [PATCH 10/24] Fix screen exit potentially occuring during transition --- osu.Game/Screens/Play/SpectatorPlayer.cs | 7 ++++++- osu.Game/Screens/Play/SpectatorResultsScreen.cs | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/SpectatorPlayer.cs b/osu.Game/Screens/Play/SpectatorPlayer.cs index bd05665ae7..7c2f8742ba 100644 --- a/osu.Game/Screens/Play/SpectatorPlayer.cs +++ b/osu.Game/Screens/Play/SpectatorPlayer.cs @@ -45,7 +45,12 @@ namespace osu.Game.Screens.Play private void userBeganPlaying(int userId, SpectatorState state) { if (userId == score.ScoreInfo.UserID) - Schedule(this.Exit); + { + Schedule(() => + { + if (this.IsCurrentScreen()) this.Exit(); + }); + } } protected override void Dispose(bool isDisposing) diff --git a/osu.Game/Screens/Play/SpectatorResultsScreen.cs b/osu.Game/Screens/Play/SpectatorResultsScreen.cs index bb1bdee3a9..56ccfd2253 100644 --- a/osu.Game/Screens/Play/SpectatorResultsScreen.cs +++ b/osu.Game/Screens/Play/SpectatorResultsScreen.cs @@ -28,7 +28,12 @@ namespace osu.Game.Screens.Play private void userBeganPlaying(int userId, SpectatorState state) { if (userId == Score.UserID) - Schedule(this.Exit); + { + Schedule(() => + { + if (this.IsCurrentScreen()) this.Exit(); + }); + } } protected override void Dispose(bool isDisposing) From 93fd9138762dfcb0a41b07d8f6b04dfe2e2b2129 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 28 Oct 2020 19:03:01 +0900 Subject: [PATCH 11/24] Add setting to allow automatically downloading during a spectating session --- osu.Game/Configuration/OsuConfigManager.cs | 3 ++ .../Settings/Sections/Online/WebSettings.cs | 6 +++ osu.Game/Screens/Play/Spectator.cs | 39 ++++++++++++++++++- 3 files changed, 46 insertions(+), 2 deletions(-) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index 7d601c0cb9..d22daf697c 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -42,6 +42,8 @@ namespace osu.Game.Configuration Set(OsuSetting.Username, string.Empty); Set(OsuSetting.Token, string.Empty); + Set(OsuSetting.AutomaticallyDownloadWhenSpectating, false); + Set(OsuSetting.SavePassword, false).ValueChanged += enabled => { if (enabled.NewValue) Set(OsuSetting.SaveUsername, true); @@ -239,5 +241,6 @@ namespace osu.Game.Configuration HitLighting, MenuBackgroundSource, GameplayDisableWinKey, + AutomaticallyDownloadWhenSpectating, } } diff --git a/osu.Game/Overlays/Settings/Sections/Online/WebSettings.cs b/osu.Game/Overlays/Settings/Sections/Online/WebSettings.cs index 6461bd7b93..8134c350a6 100644 --- a/osu.Game/Overlays/Settings/Sections/Online/WebSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Online/WebSettings.cs @@ -27,6 +27,12 @@ namespace osu.Game.Overlays.Settings.Sections.Online Keywords = new[] { "no-video" }, Current = config.GetBindable(OsuSetting.PreferNoVideo) }, + new SettingsCheckbox + { + LabelText = "Automatically download beatmaps when spectating", + Keywords = new[] { "spectator" }, + Current = config.GetBindable(OsuSetting.AutomaticallyDownloadWhenSpectating), + }, }; } } diff --git a/osu.Game/Screens/Play/Spectator.cs b/osu.Game/Screens/Play/Spectator.cs index 9b26bd5ba3..33e97bdff3 100644 --- a/osu.Game/Screens/Play/Spectator.cs +++ b/osu.Game/Screens/Play/Spectator.cs @@ -7,12 +7,14 @@ using System.Linq; using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Screens; using osu.Game.Beatmaps; +using osu.Game.Configuration; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; @@ -20,6 +22,7 @@ using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Online.Spectator; using osu.Game.Overlays.BeatmapListing.Panels; +using osu.Game.Overlays.Settings; using osu.Game.Replays; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; @@ -69,13 +72,17 @@ namespace osu.Game.Screens.Play private TriangleButton watchButton; + private SettingsCheckbox automaticDownload; + + private BeatmapSetInfo onlineBeatmap; + public Spectator([NotNull] User targetUser) { this.targetUser = targetUser ?? throw new ArgumentNullException(nameof(targetUser)); } [BackgroundDependencyLoader] - private void load(OsuColour colours) + private void load(OsuColour colours, OsuConfigManager config) { InternalChild = new Container { @@ -141,6 +148,13 @@ namespace osu.Game.Screens.Play }, } }, + automaticDownload = new SettingsCheckbox + { + LabelText = "Automatically download beatmaps", + Current = config.GetBindable(OsuSetting.AutomaticallyDownloadWhenSpectating), + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }, watchButton = new PurpleTriangleButton { Text = "Start Watching", @@ -167,6 +181,8 @@ namespace osu.Game.Screens.Play managerUpdated = beatmaps.ItemUpdated.GetBoundCopy(); managerUpdated.BindValueChanged(beatmapUpdated); + + automaticDownload.Current.BindValueChanged(_ => checkForAutomaticDownload()); } private void beatmapUpdated(ValueChangedEvent> beatmap) @@ -246,7 +262,9 @@ namespace osu.Game.Screens.Play var resolvedBeatmap = beatmaps.QueryBeatmap(b => b.OnlineBeatmapID == state.BeatmapID); if (resolvedBeatmap == null) + { return; + } if (replay == null) return; @@ -277,6 +295,7 @@ namespace osu.Game.Screens.Play if (state?.BeatmapID == null) { beatmapPanelContainer.Clear(); + onlineBeatmap = null; return; } @@ -286,12 +305,28 @@ namespace osu.Game.Screens.Play if (state != this.state) return; - beatmapPanelContainer.Child = new GridBeatmapPanel(res.ToBeatmapSet(rulesets)); + onlineBeatmap = res.ToBeatmapSet(rulesets); + beatmapPanelContainer.Child = new GridBeatmapPanel(onlineBeatmap); + checkForAutomaticDownload(); }); api.Queue(req); } + private void checkForAutomaticDownload() + { + if (onlineBeatmap == null) + return; + + if (!automaticDownload.Current.Value) + return; + + if (beatmaps.IsAvailableLocally(onlineBeatmap)) + return; + + beatmaps.Download(onlineBeatmap); + } + protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); From dd2f44f393e6cf16ef4f1a40f6b74ff4174219d9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 28 Oct 2020 19:39:38 +0900 Subject: [PATCH 12/24] Add basic "currently watching" text to player to signify that spectator is active --- osu.Game/Screens/Play/SpectatorPlayer.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/osu.Game/Screens/Play/SpectatorPlayer.cs b/osu.Game/Screens/Play/SpectatorPlayer.cs index 7c2f8742ba..2f98647d3e 100644 --- a/osu.Game/Screens/Play/SpectatorPlayer.cs +++ b/osu.Game/Screens/Play/SpectatorPlayer.cs @@ -3,8 +3,11 @@ using System.Linq; using osu.Framework.Allocation; +using osu.Framework.Graphics; using osu.Framework.Screens; using osu.Game.Beatmaps; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; using osu.Game.Online.Spectator; using osu.Game.Scoring; using osu.Game.Screens.Ranking; @@ -35,6 +38,15 @@ namespace osu.Game.Screens.Play private void load() { spectatorStreaming.OnUserBeganPlaying += userBeganPlaying; + + AddInternal(new OsuSpriteText + { + Text = $"Watching {score.ScoreInfo.User.Username} playing live!", + Font = OsuFont.Default.With(size: 30), + Y = 100, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + }); } protected override void PrepareReplay() From bca317b1518a2f67dc7592736b36552f1bda600d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 28 Oct 2020 23:43:16 +0900 Subject: [PATCH 13/24] Remove excess using statement --- osu.Game/Screens/Play/Spectator.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Screens/Play/Spectator.cs b/osu.Game/Screens/Play/Spectator.cs index 34e7bb1bda..2c8b5c4cad 100644 --- a/osu.Game/Screens/Play/Spectator.cs +++ b/osu.Game/Screens/Play/Spectator.cs @@ -7,7 +7,6 @@ using System.Linq; using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; -using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; From 4e17634ee27ab7dfc33753c1750d8f91e6e2b206 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 2 Nov 2020 17:31:04 +0900 Subject: [PATCH 14/24] Add (temporary) local user cache to avoid re-querying API each display --- .../Dashboard/CurrentlyPlayingDisplay.cs | 25 ++++++++++++++----- 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs b/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs index d71e582c05..b461da4476 100644 --- a/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs +++ b/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; using System.Collections.Specialized; using System.Linq; using osu.Framework.Allocation; @@ -43,6 +44,9 @@ namespace osu.Game.Overlays.Dashboard [Resolved] private IAPIProvider api { get; set; } + // temporary, should be game-global but i don't want to add more manager classes for now. + private static readonly Dictionary user_cache = new Dictionary(); + protected override void LoadComplete() { base.LoadComplete(); @@ -53,15 +57,24 @@ namespace osu.Game.Overlays.Dashboard switch (e.Action) { case NotifyCollectionChangedAction.Add: - foreach (var u in e.NewItems.OfType()) + foreach (int userId in e.NewItems.OfType()) { - var request = new GetUserRequest(u); - request.Success += user => Schedule(() => + if (user_cache.TryGetValue(userId, out var user)) { - if (playingUsers.Contains((int)user.Id)) - userFlow.Add(createUserPanel(user)); - }); + addUser(user); + continue; + } + + var request = new GetUserRequest(userId); + request.Success += u => Schedule(() => addUser(u)); api.Queue(request); + + void addUser(User u) + { + user_cache[userId] = u; + if (playingUsers.Contains(userId)) + userFlow.Add(createUserPanel(u)); + } } break; From 5cbfaf3589413c5be4292f97dd91c78b13059534 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 2 Nov 2020 20:19:38 +0900 Subject: [PATCH 15/24] Revert "Add (temporary) local user cache to avoid re-querying API each display" This reverts commit 4e17634ee27ab7dfc33753c1750d8f91e6e2b206. --- .../Dashboard/CurrentlyPlayingDisplay.cs | 25 +++++-------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs b/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs index b461da4476..d71e582c05 100644 --- a/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs +++ b/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Collections.Generic; using System.Collections.Specialized; using System.Linq; using osu.Framework.Allocation; @@ -44,9 +43,6 @@ namespace osu.Game.Overlays.Dashboard [Resolved] private IAPIProvider api { get; set; } - // temporary, should be game-global but i don't want to add more manager classes for now. - private static readonly Dictionary user_cache = new Dictionary(); - protected override void LoadComplete() { base.LoadComplete(); @@ -57,24 +53,15 @@ namespace osu.Game.Overlays.Dashboard switch (e.Action) { case NotifyCollectionChangedAction.Add: - foreach (int userId in e.NewItems.OfType()) + foreach (var u in e.NewItems.OfType()) { - if (user_cache.TryGetValue(userId, out var user)) + var request = new GetUserRequest(u); + request.Success += user => Schedule(() => { - addUser(user); - continue; - } - - var request = new GetUserRequest(userId); - request.Success += u => Schedule(() => addUser(u)); + if (playingUsers.Contains((int)user.Id)) + userFlow.Add(createUserPanel(user)); + }); api.Queue(request); - - void addUser(User u) - { - user_cache[userId] = u; - if (playingUsers.Contains(userId)) - userFlow.Add(createUserPanel(u)); - } } break; From c6de0544d2920440ae7aa2cc9b4f5cfed8f23c11 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 2 Nov 2020 20:21:23 +0900 Subject: [PATCH 16/24] Disable display for not --- osu.Game/Overlays/DashboardOverlay.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/DashboardOverlay.cs b/osu.Game/Overlays/DashboardOverlay.cs index 04defce636..787a4985d7 100644 --- a/osu.Game/Overlays/DashboardOverlay.cs +++ b/osu.Game/Overlays/DashboardOverlay.cs @@ -131,7 +131,8 @@ namespace osu.Game.Overlays break; case DashboardOverlayTabs.CurrentlyPlaying: - loadDisplay(new CurrentlyPlayingDisplay()); + //todo: enable once caching logic is better + //loadDisplay(new CurrentlyPlayingDisplay()); break; default: From ed30756c199989f8547dd3d9350581e417ab66ab Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 2 Nov 2020 20:41:14 +0900 Subject: [PATCH 17/24] Add test coverage for new display (and remove live version for now) --- .../Visual/Gameplay/TestSceneSpectator.cs | 9 +++ .../TestSceneCurrentlyPlayingDisplay.cs | 64 +++++++++++++++++++ .../Spectator/SpectatorStreamingClient.cs | 4 +- .../Dashboard/CurrentlyPlayingDisplay.cs | 4 +- 4 files changed, 77 insertions(+), 4 deletions(-) create mode 100644 osu.Game.Tests/Visual/Online/TestSceneCurrentlyPlayingDisplay.cs diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs index a4df450db9..558778c918 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs @@ -3,8 +3,10 @@ using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Screens; using osu.Framework.Testing; @@ -231,8 +233,15 @@ namespace osu.Game.Tests.Visual.Gameplay { public readonly User StreamingUser = new User { Id = 1234, Username = "Test user" }; + public new BindableList PlayingUsers => (BindableList)base.PlayingUsers; + private int beatmapId; + protected override Task Connect() + { + return Task.CompletedTask; + } + public void StartPlay(int beatmapId) { this.beatmapId = beatmapId; diff --git a/osu.Game.Tests/Visual/Online/TestSceneCurrentlyPlayingDisplay.cs b/osu.Game.Tests/Visual/Online/TestSceneCurrentlyPlayingDisplay.cs new file mode 100644 index 0000000000..137d0c20a3 --- /dev/null +++ b/osu.Game.Tests/Visual/Online/TestSceneCurrentlyPlayingDisplay.cs @@ -0,0 +1,64 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Testing; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; +using osu.Game.Online.Spectator; +using osu.Game.Overlays.Dashboard; +using osu.Game.Tests.Visual.Gameplay; +using osu.Game.Users; + +namespace osu.Game.Tests.Visual.Online +{ + public class TestSceneCurrentlyPlayingDisplay : OsuTestScene + { + [Cached(typeof(SpectatorStreamingClient))] + private TestSceneSpectator.TestSpectatorStreamingClient testSpectatorStreamingClient = new TestSceneSpectator.TestSpectatorStreamingClient(); + + private CurrentlyPlayingDisplay currentlyPlaying; + + private DummyAPIAccess dummyAPI => (DummyAPIAccess)API; + + [SetUpSteps] + public void SetUpSteps() + { + AddStep("register request handling", () => ((DummyAPIAccess)API).HandleRequest = req => + { + switch (req) + { + case GetUserRequest cRequest: + cRequest.TriggerSuccess(new User { Username = "peppy", Id = 2 }); + break; + } + }); + + AddStep("add streaming client", () => + { + Remove(testSpectatorStreamingClient); + + Children = new Drawable[] + { + testSpectatorStreamingClient, + currentlyPlaying = new CurrentlyPlayingDisplay + { + RelativeSizeAxes = Axes.Both, + } + }; + }); + } + + [Test] + public void TestBasicDisplay() + { + AddStep("Add playing user", () => testSpectatorStreamingClient.PlayingUsers.Add(2)); + AddUntilStep("Panel loaded", () => currentlyPlaying.ChildrenOfType()?.FirstOrDefault()?.User.Id == 2); + AddStep("Remove playing user", () => testSpectatorStreamingClient.PlayingUsers.Remove(2)); + AddUntilStep("Panel no longer present", () => !currentlyPlaying.ChildrenOfType().Any()); + } + } +} diff --git a/osu.Game/Online/Spectator/SpectatorStreamingClient.cs b/osu.Game/Online/Spectator/SpectatorStreamingClient.cs index cb170ad298..c07c00f3ba 100644 --- a/osu.Game/Online/Spectator/SpectatorStreamingClient.cs +++ b/osu.Game/Online/Spectator/SpectatorStreamingClient.cs @@ -92,14 +92,14 @@ namespace osu.Game.Online.Spectator break; case APIState.Online: - Task.Run(connect); + Task.Run(Connect); break; } } private const string endpoint = "https://spectator.ppy.sh/spectator"; - private async Task connect() + protected virtual async Task Connect() { if (connection != null) return; diff --git a/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs b/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs index d71e582c05..cb88065ec6 100644 --- a/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs +++ b/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs @@ -78,7 +78,7 @@ namespace osu.Game.Overlays.Dashboard }), true); } - [Resolved] + [Resolved(canBeNull: true)] private OsuGame game { get; set; } private UserPanel createUserPanel(User user) @@ -89,7 +89,7 @@ namespace osu.Game.Overlays.Dashboard panel.Origin = Anchor.TopCentre; panel.Width = 290; panel.ShowProfileOnClick = false; - panel.Action = () => game.PerformFromScreen(s => s.Push(new Spectator(user))); + panel.Action = () => game?.PerformFromScreen(s => s.Push(new Spectator(user))); }); } } From c1d9a0c92c1c33a229a4df1a28865b2396a3412d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 2 Nov 2020 21:09:47 +0900 Subject: [PATCH 18/24] Move click action out of user panel --- .../Visual/Gameplay/TestSceneSpectator.cs | 2 +- .../Dashboard/CurrentlyPlayingDisplay.cs | 57 +++++++++++++++---- osu.Game/Users/UserPanel.cs | 9 +-- 3 files changed, 52 insertions(+), 16 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs index 558778c918..df4b85b37a 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs @@ -229,7 +229,7 @@ namespace osu.Game.Tests.Visual.Gameplay AddUntilStep("wait for screen load", () => spectatorScreen.LoadState == LoadState.Loaded); } - internal class TestSpectatorStreamingClient : SpectatorStreamingClient + public class TestSpectatorStreamingClient : SpectatorStreamingClient { public readonly User StreamingUser = new User { Id = 1234, Username = "Test user" }; diff --git a/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs b/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs index cb88065ec6..8832cb52ea 100644 --- a/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs +++ b/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs @@ -11,6 +11,7 @@ using osu.Framework.Screens; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Online.Spectator; +using osu.Game.Screens.Multi.Match.Components; using osu.Game.Screens.Play; using osu.Game.Users; using osuTK; @@ -21,7 +22,7 @@ namespace osu.Game.Overlays.Dashboard { private IBindableList playingUsers; - private FillFlowContainer userFlow; + private FillFlowContainer userFlow; [Resolved] private SpectatorStreamingClient spectatorStreaming { get; set; } @@ -32,7 +33,7 @@ namespace osu.Game.Overlays.Dashboard RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; - InternalChild = userFlow = new FillFlowContainer + InternalChild = userFlow = new FillFlowContainer { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, @@ -78,19 +79,53 @@ namespace osu.Game.Overlays.Dashboard }), true); } - [Resolved(canBeNull: true)] - private OsuGame game { get; set; } - - private UserPanel createUserPanel(User user) - { - return new UserGridPanel(user).With(panel => + private PlayingUserPanel createUserPanel(User user) => + new PlayingUserPanel(user).With(panel => { panel.Anchor = Anchor.TopCentre; panel.Origin = Anchor.TopCentre; - panel.Width = 290; - panel.ShowProfileOnClick = false; - panel.Action = () => game?.PerformFromScreen(s => s.Push(new Spectator(user))); }); + + private class PlayingUserPanel : CompositeDrawable + { + public readonly User User; + + [Resolved(canBeNull: true)] + private OsuGame game { get; set; } + + public PlayingUserPanel(User user) + { + User = user; + + AutoSizeAxes = Axes.Both; + + InternalChildren = new Drawable[] + { + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Spacing = new Vector2(2), + Children = new Drawable[] + { + new UserGridPanel(user) + { + Width = 290, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }, + new PurpleTriangleButton + { + Width = 290, + Text = "Watch", + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Action = () => game?.PerformFromScreen(s => s.Push(new Spectator(user))) + } + } + }, + }; + } } } } diff --git a/osu.Game/Users/UserPanel.cs b/osu.Game/Users/UserPanel.cs index e97ff4287f..0981136dba 100644 --- a/osu.Game/Users/UserPanel.cs +++ b/osu.Game/Users/UserPanel.cs @@ -20,10 +20,12 @@ namespace osu.Game.Users { public readonly User User; + /// + /// Perform an action in addition to showing the user's profile. + /// This should be used to perform auxiliary tasks and not as a primary action for clicking a user panel (to maintain a consistent UX). + /// public new Action Action; - public bool ShowProfileOnClick = true; - protected Action ViewProfile { get; private set; } protected Drawable Background { get; private set; } @@ -70,8 +72,7 @@ namespace osu.Game.Users base.Action = ViewProfile = () => { Action?.Invoke(); - if (ShowProfileOnClick) - profileOverlay?.ShowUser(User); + profileOverlay?.ShowUser(User); }; } From d4f8c63f9ee1de0cfcfbaa8b6b6206e42e16ee64 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 3 Nov 2020 19:59:06 +0900 Subject: [PATCH 19/24] Fix reference to dummyAPI not using helper property --- .../Visual/Online/TestSceneCurrentlyPlayingDisplay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneCurrentlyPlayingDisplay.cs b/osu.Game.Tests/Visual/Online/TestSceneCurrentlyPlayingDisplay.cs index 137d0c20a3..f85abeb51a 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneCurrentlyPlayingDisplay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneCurrentlyPlayingDisplay.cs @@ -27,7 +27,7 @@ namespace osu.Game.Tests.Visual.Online [SetUpSteps] public void SetUpSteps() { - AddStep("register request handling", () => ((DummyAPIAccess)API).HandleRequest = req => + AddStep("register request handling", () => dummyAPI.HandleRequest = req => { switch (req) { From aaffd59dfe4a43ebe1d75802355f0ca6453e07f1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 3 Nov 2020 20:02:02 +0900 Subject: [PATCH 20/24] Add test step to reset players (to better allow multiple runs of tests) --- .../Visual/Online/TestSceneCurrentlyPlayingDisplay.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Tests/Visual/Online/TestSceneCurrentlyPlayingDisplay.cs b/osu.Game.Tests/Visual/Online/TestSceneCurrentlyPlayingDisplay.cs index f85abeb51a..d6fd33bce7 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneCurrentlyPlayingDisplay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneCurrentlyPlayingDisplay.cs @@ -50,6 +50,8 @@ namespace osu.Game.Tests.Visual.Online } }; }); + + AddStep("Reset players", () => testSpectatorStreamingClient.PlayingUsers.Clear()); } [Test] From 3e29e468ea3907bad4af44c08e24ad74c17eeeb2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 3 Nov 2020 20:06:42 +0900 Subject: [PATCH 21/24] Ensure "start watching" button starts in a disabled state --- osu.Game/Screens/Play/Spectator.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/Spectator.cs b/osu.Game/Screens/Play/Spectator.cs index 2c8b5c4cad..9ed911efd5 100644 --- a/osu.Game/Screens/Play/Spectator.cs +++ b/osu.Game/Screens/Play/Spectator.cs @@ -165,7 +165,8 @@ namespace osu.Game.Screens.Play Width = 250, Anchor = Anchor.Centre, Origin = Anchor.Centre, - Action = attemptStart + Action = attemptStart, + Enabled = { Value = false } } } } From 1b2bd6a8c9de4abd0539bd217f5db55f65939ce1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 3 Nov 2020 20:10:13 +0900 Subject: [PATCH 22/24] Remove redundant base call --- osu.Game/Screens/Play/SpectatorPlayer.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Screens/Play/SpectatorPlayer.cs b/osu.Game/Screens/Play/SpectatorPlayer.cs index fbed84b820..fdf996150f 100644 --- a/osu.Game/Screens/Play/SpectatorPlayer.cs +++ b/osu.Game/Screens/Play/SpectatorPlayer.cs @@ -21,7 +21,6 @@ namespace osu.Game.Screens.Play protected override bool CheckModsAllowFailure() => false; // todo: better support starting mid-way through beatmap public SpectatorPlayer(Score score) - : base(true, true) { this.score = score; } From 86d283ebf4439c940a02f5fd889ff1f27d406447 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 3 Nov 2020 19:03:48 +0100 Subject: [PATCH 23/24] Adjust layout slightly to avoid specifying width twice --- osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs b/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs index 8832cb52ea..79af81cfac 100644 --- a/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs +++ b/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs @@ -103,20 +103,21 @@ namespace osu.Game.Overlays.Dashboard { new FillFlowContainer { - AutoSizeAxes = Axes.Both, + AutoSizeAxes = Axes.Y, Direction = FillDirection.Vertical, Spacing = new Vector2(2), + Width = 290, Children = new Drawable[] { new UserGridPanel(user) { - Width = 290, + RelativeSizeAxes = Axes.X, Anchor = Anchor.Centre, Origin = Anchor.Centre, }, new PurpleTriangleButton { - Width = 290, + RelativeSizeAxes = Axes.X, Text = "Watch", Anchor = Anchor.Centre, Origin = Anchor.Centre, From 211510fe9aa0eff1243249cb3aeb4b2cf8519f59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 3 Nov 2020 19:12:03 +0100 Subject: [PATCH 24/24] Fix undesirable vertical spacing in currently playing display --- osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs b/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs index 79af81cfac..34444c2fa5 100644 --- a/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs +++ b/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs @@ -112,15 +112,15 @@ namespace osu.Game.Overlays.Dashboard new UserGridPanel(user) { RelativeSizeAxes = Axes.X, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, }, new PurpleTriangleButton { RelativeSizeAxes = Axes.X, Text = "Watch", - Anchor = Anchor.Centre, - Origin = Anchor.Centre, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, Action = () => game?.PerformFromScreen(s => s.Push(new Spectator(user))) } }