mirror of
https://github.com/ppy/osu.git
synced 2025-02-13 19:12:54 +08:00
Make SoloSpectator use the new SpectatorScreen class
This commit is contained in:
parent
9e95441aa6
commit
9bc2a486e0
@ -1,13 +1,9 @@
|
||||
// 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 System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
@ -24,73 +20,54 @@ 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;
|
||||
using osu.Game.Rulesets.Replays;
|
||||
using osu.Game.Rulesets.Replays.Types;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Screens.OnlinePlay.Match.Components;
|
||||
using osu.Game.Screens.Spectate;
|
||||
using osu.Game.Users;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Screens.Play
|
||||
{
|
||||
[Cached(typeof(IPreviewTrackOwner))]
|
||||
public class SoloSpectator : OsuScreen, IPreviewTrackOwner
|
||||
public class SoloSpectator : SpectatorScreen, IPreviewTrackOwner
|
||||
{
|
||||
[NotNull]
|
||||
private readonly User targetUser;
|
||||
|
||||
[Resolved]
|
||||
private Bindable<WorkingBeatmap> beatmap { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private Bindable<RulesetInfo> ruleset { get; set; }
|
||||
|
||||
private Ruleset rulesetInstance;
|
||||
|
||||
[Resolved]
|
||||
private Bindable<IReadOnlyList<Mod>> mods { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private IAPIProvider api { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private SpectatorStreamingClient spectatorStreaming { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private BeatmapManager beatmaps { get; set; }
|
||||
private PreviewTrackManager previewTrackManager { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private RulesetStore rulesets { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private PreviewTrackManager previewTrackManager { get; set; }
|
||||
|
||||
private Score score;
|
||||
|
||||
private readonly object scoreLock = new object();
|
||||
private BeatmapManager beatmaps { get; set; }
|
||||
|
||||
private Container beatmapPanelContainer;
|
||||
|
||||
private SpectatorState state;
|
||||
|
||||
private IBindable<WeakReference<BeatmapSetInfo>> managerUpdated;
|
||||
|
||||
private TriangleButton watchButton;
|
||||
|
||||
private SettingsCheckbox automaticDownload;
|
||||
|
||||
private BeatmapSetInfo onlineBeatmap;
|
||||
|
||||
/// <summary>
|
||||
/// Becomes true if a new state is waiting to be loaded (while this screen was not active).
|
||||
/// The player's immediate online gameplay state.
|
||||
/// This doesn't reflect the gameplay state being watched by the user if <see cref="pendingGameplayState"/> is non-null.
|
||||
/// </summary>
|
||||
private bool newStatePending;
|
||||
private GameplayState immediateGameplayState;
|
||||
|
||||
/// <summary>
|
||||
/// The gameplay state that is pending to be watched, upon this screen becoming current.
|
||||
/// </summary>
|
||||
private GameplayState pendingGameplayState;
|
||||
|
||||
private GetBeatmapSetRequest onlineBeatmapRequest;
|
||||
|
||||
public SoloSpectator([NotNull] User targetUser)
|
||||
: base(targetUser.Id)
|
||||
{
|
||||
this.targetUser = targetUser ?? throw new ArgumentNullException(nameof(targetUser));
|
||||
this.targetUser = targetUser;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
@ -173,7 +150,7 @@ namespace osu.Game.Screens.Play
|
||||
Width = 250,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Action = attemptStart,
|
||||
Action = () => attemptStart(immediateGameplayState),
|
||||
Enabled = { Value = false }
|
||||
}
|
||||
}
|
||||
@ -185,169 +162,81 @@ namespace osu.Game.Screens.Play
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
spectatorStreaming.OnUserBeganPlaying += userBeganPlaying;
|
||||
spectatorStreaming.OnUserFinishedPlaying += userFinishedPlaying;
|
||||
spectatorStreaming.OnNewFrames += userSentFrames;
|
||||
|
||||
spectatorStreaming.WatchUser(targetUser.Id);
|
||||
|
||||
managerUpdated = beatmaps.ItemUpdated.GetBoundCopy();
|
||||
managerUpdated.BindValueChanged(beatmapUpdated);
|
||||
|
||||
automaticDownload.Current.BindValueChanged(_ => checkForAutomaticDownload());
|
||||
}
|
||||
|
||||
private void beatmapUpdated(ValueChangedEvent<WeakReference<BeatmapSetInfo>> beatmap)
|
||||
protected override void OnUserStateChanged(int userId, SpectatorState spectatorState) => Schedule(() =>
|
||||
{
|
||||
if (beatmap.NewValue.TryGetTarget(out var beatmapSet) && beatmapSet.Beatmaps.Any(b => b.OnlineBeatmapID == state.BeatmapID))
|
||||
Schedule(attemptStart);
|
||||
}
|
||||
clearDisplay();
|
||||
showBeatmapPanel(spectatorState);
|
||||
});
|
||||
|
||||
private void userSentFrames(int userId, FrameDataBundle data)
|
||||
protected override void StartGameplay(int userId, GameplayState gameplayState) => Schedule(() =>
|
||||
{
|
||||
// this is not scheduled as it handles propagation of frames even when in a child screen (at which point we are not alive).
|
||||
// probably not the safest way to handle this.
|
||||
|
||||
if (userId != targetUser.Id)
|
||||
return;
|
||||
|
||||
lock (scoreLock)
|
||||
{
|
||||
// this should never happen as the server sends the user's state on watching,
|
||||
// but is here as a safety measure.
|
||||
if (score == null)
|
||||
return;
|
||||
|
||||
// rulesetInstance should be guaranteed to be in sync with the score via scoreLock.
|
||||
Debug.Assert(rulesetInstance != null && rulesetInstance.RulesetInfo.Equals(score.ScoreInfo.Ruleset));
|
||||
|
||||
foreach (var frame in data.Frames)
|
||||
{
|
||||
IConvertibleReplayFrame convertibleFrame = rulesetInstance.CreateConvertibleReplayFrame();
|
||||
convertibleFrame.FromLegacy(frame, beatmap.Value.Beatmap);
|
||||
|
||||
var convertedFrame = (ReplayFrame)convertibleFrame;
|
||||
convertedFrame.Time = frame.Time;
|
||||
|
||||
score.Replay.Frames.Add(convertedFrame);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void userBeganPlaying(int userId, SpectatorState state)
|
||||
{
|
||||
if (userId != targetUser.Id)
|
||||
return;
|
||||
|
||||
this.state = state;
|
||||
pendingGameplayState = null;
|
||||
immediateGameplayState = gameplayState;
|
||||
|
||||
if (this.IsCurrentScreen())
|
||||
Schedule(attemptStart);
|
||||
attemptStart(gameplayState);
|
||||
else
|
||||
newStatePending = true;
|
||||
}
|
||||
pendingGameplayState = gameplayState;
|
||||
|
||||
watchButton.Enabled.Value = true;
|
||||
});
|
||||
|
||||
protected override void EndGameplay(int userId) => Schedule(() =>
|
||||
{
|
||||
pendingGameplayState = null;
|
||||
immediateGameplayState = null;
|
||||
|
||||
Schedule(clearDisplay);
|
||||
|
||||
watchButton.Enabled.Value = false;
|
||||
});
|
||||
|
||||
public override void OnResuming(IScreen last)
|
||||
{
|
||||
base.OnResuming(last);
|
||||
|
||||
if (newStatePending)
|
||||
if (pendingGameplayState != null)
|
||||
{
|
||||
attemptStart();
|
||||
newStatePending = false;
|
||||
attemptStart(pendingGameplayState);
|
||||
pendingGameplayState = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void userFinishedPlaying(int userId, SpectatorState state)
|
||||
{
|
||||
if (userId != targetUser.Id)
|
||||
return;
|
||||
|
||||
lock (scoreLock)
|
||||
{
|
||||
if (score != null)
|
||||
{
|
||||
score.Replay.HasReceivedAllFrames = true;
|
||||
score = null;
|
||||
}
|
||||
}
|
||||
|
||||
Schedule(clearDisplay);
|
||||
}
|
||||
|
||||
private void clearDisplay()
|
||||
{
|
||||
watchButton.Enabled.Value = false;
|
||||
onlineBeatmapRequest?.Cancel();
|
||||
beatmapPanelContainer.Clear();
|
||||
previewTrackManager.StopAnyPlaying(this);
|
||||
}
|
||||
|
||||
private void attemptStart()
|
||||
private void attemptStart(GameplayState gameplayState)
|
||||
{
|
||||
clearDisplay();
|
||||
showBeatmapPanel(state);
|
||||
|
||||
var resolvedRuleset = rulesets.AvailableRulesets.FirstOrDefault(r => r.ID == state.RulesetID)?.CreateInstance();
|
||||
|
||||
// ruleset not available
|
||||
if (resolvedRuleset == null)
|
||||
if (gameplayState == null)
|
||||
return;
|
||||
|
||||
if (state.BeatmapID == null)
|
||||
return;
|
||||
Beatmap.Value = gameplayState.Beatmap;
|
||||
Ruleset.Value = gameplayState.Ruleset.RulesetInfo;
|
||||
|
||||
var resolvedBeatmap = beatmaps.QueryBeatmap(b => b.OnlineBeatmapID == state.BeatmapID);
|
||||
|
||||
if (resolvedBeatmap == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
lock (scoreLock)
|
||||
{
|
||||
score = new Score
|
||||
{
|
||||
ScoreInfo = new ScoreInfo
|
||||
{
|
||||
Beatmap = resolvedBeatmap,
|
||||
User = targetUser,
|
||||
Mods = state.Mods.Select(m => m.ToMod(resolvedRuleset)).ToArray(),
|
||||
Ruleset = resolvedRuleset.RulesetInfo,
|
||||
},
|
||||
Replay = new Replay { HasReceivedAllFrames = false },
|
||||
};
|
||||
|
||||
ruleset.Value = resolvedRuleset.RulesetInfo;
|
||||
rulesetInstance = resolvedRuleset;
|
||||
|
||||
beatmap.Value = beatmaps.GetWorkingBeatmap(resolvedBeatmap);
|
||||
watchButton.Enabled.Value = true;
|
||||
|
||||
this.Push(new SpectatorPlayerLoader(score));
|
||||
}
|
||||
this.Push(new SpectatorPlayerLoader(gameplayState.Score));
|
||||
}
|
||||
|
||||
private void showBeatmapPanel(SpectatorState state)
|
||||
{
|
||||
if (state?.BeatmapID == null)
|
||||
{
|
||||
onlineBeatmap = null;
|
||||
return;
|
||||
}
|
||||
Debug.Assert(state.BeatmapID != null);
|
||||
|
||||
var req = new GetBeatmapSetRequest(state.BeatmapID.Value, BeatmapSetLookupType.BeatmapId);
|
||||
req.Success += res => Schedule(() =>
|
||||
onlineBeatmapRequest = new GetBeatmapSetRequest(state.BeatmapID.Value, BeatmapSetLookupType.BeatmapId);
|
||||
onlineBeatmapRequest.Success += res => Schedule(() =>
|
||||
{
|
||||
if (state != this.state)
|
||||
return;
|
||||
|
||||
onlineBeatmap = res.ToBeatmapSet(rulesets);
|
||||
beatmapPanelContainer.Child = new GridBeatmapPanel(onlineBeatmap);
|
||||
checkForAutomaticDownload();
|
||||
});
|
||||
|
||||
api.Queue(req);
|
||||
api.Queue(onlineBeatmapRequest);
|
||||
}
|
||||
|
||||
private void checkForAutomaticDownload()
|
||||
@ -369,21 +258,5 @@ namespace osu.Game.Screens.Play
|
||||
previewTrackManager.StopAnyPlaying(this);
|
||||
return base.OnExiting(next);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
|
||||
if (spectatorStreaming != null)
|
||||
{
|
||||
spectatorStreaming.OnUserBeganPlaying -= userBeganPlaying;
|
||||
spectatorStreaming.OnUserFinishedPlaying -= userFinishedPlaying;
|
||||
spectatorStreaming.OnNewFrames -= userSentFrames;
|
||||
|
||||
spectatorStreaming.StopWatchingUser(targetUser.Id);
|
||||
}
|
||||
|
||||
managerUpdated?.UnbindAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user