1
0
mirror of https://github.com/ppy/osu.git synced 2026-05-22 16:10:24 +08:00
Files
osu-lazer/osu.Game/Screens/Play/SpectatorPlayer.cs
T
Bartłomiej Dach 1edbb1d586 Show leaderboard in solo spectator
Closes https://github.com/ppy/osu/issues/35293.

The removed comment here says that "there's no guarantee" that
`LeaderboardManager` has the correct scores:

	https://github.com/ppy/osu/blob/a060ddb5439412e8b503800f4e6e8d80df40cacf/osu.Game/Screens/Play/SpectatorPlayer.cs#L22-L25

but at least now, that's not correct because this is ensured via

	https://github.com/ppy/osu/blob/a060ddb5439412e8b503800f4e6e8d80df40cacf/osu.Game/Screens/Play/PlayerLoader.cs#L280-L286

through which the solo spectator player is pushed:

	https://github.com/ppy/osu/blob/a060ddb5439412e8b503800f4e6e8d80df40cacf/osu.Game/Screens/Play/SoloSpectatorScreen.cs#L239

The empty leaderboard provider is still valid however in the case of
multiplayer spectator, because there we don't really ever want to be
showing any leaderboards on the individual player instances.
2025-10-14 11:50:55 +02:00

123 lines
3.8 KiB
C#

// 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.
using osu.Framework.Allocation;
using osu.Framework.Extensions.ObjectExtensions;
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.Rulesets.Replays;
using osu.Game.Rulesets.Replays.Types;
using osu.Game.Scoring;
using osu.Game.Screens.Ranking;
namespace osu.Game.Screens.Play
{
public abstract partial class SpectatorPlayer : Player
{
[Resolved]
protected SpectatorClient SpectatorClient { get; private set; } = null!;
private readonly Score score;
protected SpectatorPlayer(Score score, PlayerConfiguration? configuration = null)
: base(configuration)
{
this.score = score;
}
[BackgroundDependencyLoader]
private void load()
{
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 LoadComplete()
{
base.LoadComplete();
DrawableRuleset.FrameStableClock.WaitingOnFrames.BindValueChanged(waiting =>
{
if (GameplayClockContainer is MasterGameplayClockContainer master)
{
if (master.UserPlaybackRate.Value > 1 && waiting.NewValue)
master.UserPlaybackRate.Value = 1;
}
}, true);
}
protected override void StartGameplay()
{
base.StartGameplay();
// Start gameplay along with the very first arrival frame (the latest one).
score.Replay.Frames.Clear();
SpectatorClient.OnNewFrames += userSentFrames;
}
private void userSentFrames(int userId, FrameDataBundle bundle)
{
if (userId != score.ScoreInfo.User.OnlineID)
return;
if (!LoadedBeatmapSuccessfully)
return;
if (!this.IsCurrentScreen())
return;
bool isFirstBundle = score.Replay.Frames.Count == 0;
foreach (var frame in bundle.Frames)
{
IConvertibleReplayFrame convertibleFrame = GameplayState.Ruleset.CreateConvertibleReplayFrame()!;
convertibleFrame.FromLegacy(frame, GameplayState.Beatmap);
var convertedFrame = (ReplayFrame)convertibleFrame;
convertedFrame.Time = frame.Time;
convertedFrame.Header = frame.Header;
score.Replay.Frames.Add(convertedFrame);
}
if (isFirstBundle && score.Replay.Frames.Count > 0)
SetGameplayStartTime(score.Replay.Frames[0].Time);
}
protected override Score CreateScore(IBeatmap beatmap) => score;
protected override ResultsScreen CreateResults(ScoreInfo score)
=> new SpectatorResultsScreen(score);
protected override void PrepareReplay()
{
DrawableRuleset?.SetReplayScore(score);
}
public override bool OnExiting(ScreenExitEvent e)
{
SpectatorClient.OnNewFrames -= userSentFrames;
return base.OnExiting(e);
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
if (SpectatorClient.IsNotNull())
SpectatorClient.OnNewFrames -= userSentFrames;
}
}
}