diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs index 72c6fd8d44..3e5b561a6f 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs @@ -17,6 +17,7 @@ using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Replays; using osu.Game.Rulesets.UI; +using osu.Game.Scoring; using osu.Game.Screens.Play; using osu.Game.Tests.Beatmaps.IO; using osu.Game.Users; @@ -272,7 +273,7 @@ public void SendFrames(int index, int count) frames.Add(new LegacyReplayFrame(i * 100, RNG.Next(0, 512), RNG.Next(0, 512), buttonState)); } - var bundle = new FrameDataBundle(frames); + var bundle = new FrameDataBundle(new ScoreInfo(), frames); ((ISpectatorClient)this).UserSentFrames(StreamingUser.Id, bundle); if (!sentState) diff --git a/osu.Game/Online/Spectator/FrameDataBundle.cs b/osu.Game/Online/Spectator/FrameDataBundle.cs index 5281e61f9c..fecf88de22 100644 --- a/osu.Game/Online/Spectator/FrameDataBundle.cs +++ b/osu.Game/Online/Spectator/FrameDataBundle.cs @@ -1,20 +1,26 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +#nullable enable + using System; using System.Collections.Generic; using osu.Game.Replays.Legacy; +using osu.Game.Scoring; namespace osu.Game.Online.Spectator { [Serializable] public class FrameDataBundle { + public FrameHeader Header { get; set; } + public IEnumerable Frames { get; set; } - public FrameDataBundle(IEnumerable frames) + public FrameDataBundle(ScoreInfo score, IEnumerable frames) { Frames = frames; + Header = new FrameHeader(score); } } } diff --git a/osu.Game/Online/Spectator/FrameHeader.cs b/osu.Game/Online/Spectator/FrameHeader.cs new file mode 100644 index 0000000000..0940eefa40 --- /dev/null +++ b/osu.Game/Online/Spectator/FrameHeader.cs @@ -0,0 +1,35 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +#nullable enable + +using System; +using System.Collections.Generic; +using osu.Game.Rulesets.Scoring; +using osu.Game.Scoring; + +namespace osu.Game.Online.Spectator +{ + [Serializable] + public class FrameHeader + { + public int Combo { get; set; } + + public int MaxCombo { get; set; } + + public Dictionary Statistics = new Dictionary(); + + /// + /// Construct header summary information from a point-in-time reference to a score which is actively being played. + /// + /// The score for reference. + public FrameHeader(ScoreInfo score) + { + Combo = score.Combo; + MaxCombo = score.MaxCombo; + + foreach (var kvp in score.Statistics) + Statistics[kvp.Key] = kvp.Value; + } + } +} diff --git a/osu.Game/Online/Spectator/SpectatorStreamingClient.cs b/osu.Game/Online/Spectator/SpectatorStreamingClient.cs index 08b524087a..0167a5d025 100644 --- a/osu.Game/Online/Spectator/SpectatorStreamingClient.cs +++ b/osu.Game/Online/Spectator/SpectatorStreamingClient.cs @@ -21,6 +21,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.Play; namespace osu.Game.Online.Spectator @@ -52,6 +53,9 @@ public class SpectatorStreamingClient : Component, ISpectatorClient [CanBeNull] private IBeatmap currentBeatmap; + [CanBeNull] + private Score currentScore; + [Resolved] private IBindable currentRuleset { get; set; } @@ -203,7 +207,7 @@ Task ISpectatorClient.UserSentFrames(int userId, FrameDataBundle data) return Task.CompletedTask; } - public void BeginPlaying(GameplayBeatmap beatmap) + public void BeginPlaying(GameplayBeatmap beatmap, Score score) { if (isPlaying) throw new InvalidOperationException($"Cannot invoke {nameof(BeginPlaying)} when already playing"); @@ -216,6 +220,8 @@ public void BeginPlaying(GameplayBeatmap beatmap) currentState.Mods = currentMods.Value.Select(m => new APIMod(m)); currentBeatmap = beatmap.PlayableBeatmap; + currentScore = score; + beginPlaying(); } @@ -308,7 +314,9 @@ private void purgePendingFrames() pendingFrames.Clear(); - SendFrames(new FrameDataBundle(frames)); + Debug.Assert(currentScore != null); + + SendFrames(new FrameDataBundle(currentScore.ScoreInfo, frames)); lastSendTime = Time.Current; } diff --git a/osu.Game/Rulesets/UI/ReplayRecorder.cs b/osu.Game/Rulesets/UI/ReplayRecorder.cs index 2918a3b445..a4d46e3888 100644 --- a/osu.Game/Rulesets/UI/ReplayRecorder.cs +++ b/osu.Game/Rulesets/UI/ReplayRecorder.cs @@ -49,7 +49,7 @@ protected override void LoadComplete() inputManager = GetContainingInputManager(); - spectatorStreaming?.BeginPlaying(gameplayBeatmap); + spectatorStreaming?.BeginPlaying(gameplayBeatmap, target); } protected override void Dispose(bool isDisposing)