1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-17 02:12:54 +08:00

Merge branch 'spectator-replay-watcher' into spectator-listing

This commit is contained in:
Dean Herbert 2020-10-29 15:13:21 +09:00
commit 0818835b16
2 changed files with 60 additions and 20 deletions

View File

@ -61,13 +61,8 @@ namespace osu.Game.Tests.Visual.Gameplay
finish(); finish();
} }
private OsuFramedReplayInputHandler replayHandler =>
(OsuFramedReplayInputHandler)Stack.ChildrenOfType<OsuInputManager>().First().ReplayInputHandler;
private Player player => Stack.CurrentScreen as Player;
[Test] [Test]
public void TestBasicSpectatingFlow() public void TestFrameStarvationAndResume()
{ {
loadSpectatingScreen(); loadSpectatingScreen();
@ -82,11 +77,16 @@ namespace osu.Game.Tests.Visual.Gameplay
AddUntilStep("wait for frame starvation", () => replayHandler.NextFrame == null); AddUntilStep("wait for frame starvation", () => replayHandler.NextFrame == null);
checkPaused(true); checkPaused(true);
double? pausedTime = null;
AddStep("store time", () => pausedTime = currentFrameStableTime);
sendFrames(); sendFrames();
checkPaused(false);
AddUntilStep("wait for frame starvation", () => replayHandler.NextFrame == null); AddUntilStep("wait for frame starvation", () => replayHandler.NextFrame == null);
checkPaused(true); checkPaused(true);
AddAssert("time advanced", () => currentFrameStableTime > pausedTime);
} }
[Test] [Test]
@ -98,7 +98,7 @@ namespace osu.Game.Tests.Visual.Gameplay
waitForPlayer(); waitForPlayer();
checkPaused(true); checkPaused(true);
sendFrames(); sendFrames(1000); // send enough frames to ensure play won't be paused
checkPaused(false); checkPaused(false);
} }
@ -119,15 +119,23 @@ namespace osu.Game.Tests.Visual.Gameplay
} }
[Test] [Test]
public void TestHostStartsPlayingWhileAlreadyWatching() public void TestHostRetriesWhileWatching()
{ {
loadSpectatingScreen(); loadSpectatingScreen();
start(); start();
sendFrames(); sendFrames();
waitForPlayer();
Player lastPlayer = null;
AddStep("store first player", () => lastPlayer = player);
start(); start();
sendFrames(); sendFrames();
waitForPlayer();
AddAssert("player is different", () => lastPlayer != player);
} }
[Test] [Test]
@ -155,9 +163,25 @@ namespace osu.Game.Tests.Visual.Gameplay
sendFrames(); sendFrames();
waitForPlayer(); waitForPlayer();
// should immediately exit and unbind from streaming client
AddStep("stop spectating", () => (Stack.CurrentScreen as Player)?.Exit()); AddStep("stop spectating", () => (Stack.CurrentScreen as Player)?.Exit());
AddUntilStep("spectating stopped", () => spectatorScreen.GetParentScreen() == null); AddUntilStep("spectating stopped", () => spectatorScreen.GetChildScreen() == null);
}
[Test]
public void TestStopWatchingThenHostRetries()
{
loadSpectatingScreen();
start();
sendFrames();
waitForPlayer();
AddStep("stop spectating", () => (Stack.CurrentScreen as Player)?.Exit());
AddUntilStep("spectating stopped", () => spectatorScreen.GetChildScreen() == null);
// host starts playing a new session
start();
waitForPlayer();
} }
[Test] [Test]
@ -171,6 +195,14 @@ namespace osu.Game.Tests.Visual.Gameplay
AddAssert("screen didn't change", () => Stack.CurrentScreen is Spectator); AddAssert("screen didn't change", () => Stack.CurrentScreen is Spectator);
} }
private OsuFramedReplayInputHandler replayHandler =>
(OsuFramedReplayInputHandler)Stack.ChildrenOfType<OsuInputManager>().First().ReplayInputHandler;
private Player player => Stack.CurrentScreen as Player;
private double currentFrameStableTime
=> player.ChildrenOfType<FrameStabilityContainer>().First().FrameStableClock.CurrentTime;
private void waitForPlayer() => AddUntilStep("wait for player", () => Stack.CurrentScreen is Player); private void waitForPlayer() => AddUntilStep("wait for player", () => Stack.CurrentScreen is Player);
private void start(int? beatmapId = null) => AddStep("start play", () => testSpectatorStreamingClient.StartPlay(beatmapId ?? importedBeatmapId)); private void start(int? beatmapId = null) => AddStep("start play", () => testSpectatorStreamingClient.StartPlay(beatmapId ?? importedBeatmapId));

View File

@ -85,20 +85,31 @@ namespace osu.Game.Rulesets.UI
public override bool UpdateSubTree() public override bool UpdateSubTree()
{ {
state = frameStableClock.IsPaused.Value ? PlaybackState.NotValid : PlaybackState.Valid; double proposedTime = manualClock.CurrentTime;
if (frameStableClock.WaitingOnFrames.Value) if (frameStableClock.WaitingOnFrames.Value)
{ {
// for now, force one update loop to check if frames have arrived // when waiting on frames, the update loop still needs to be run (at least once) to check for newly arrived frames.
// this may have to change in the future where we want stable user pausing during replay playback. // time should not be sourced from the parent clock in this case.
state = PlaybackState.Valid; state = PlaybackState.Valid;
} }
else if (!frameStableClock.IsPaused.Value)
{
state = PlaybackState.Valid;
proposedTime = parentGameplayClock.CurrentTime;
}
else
{
// time should not advance while paused, not should anything run.
state = PlaybackState.NotValid;
return true;
}
int loops = MaxCatchUpFrames; int loops = MaxCatchUpFrames;
while (state != PlaybackState.NotValid && loops-- > 0) while (loops-- > 0)
{ {
updateClock(); updateClock(ref proposedTime);
if (state == PlaybackState.NotValid) if (state == PlaybackState.NotValid)
break; break;
@ -110,7 +121,7 @@ namespace osu.Game.Rulesets.UI
return true; return true;
} }
private void updateClock() private void updateClock(ref double proposedTime)
{ {
if (parentGameplayClock == null) if (parentGameplayClock == null)
setClock(); // LoadComplete may not be run yet, but we still want the clock. setClock(); // LoadComplete may not be run yet, but we still want the clock.
@ -118,9 +129,6 @@ namespace osu.Game.Rulesets.UI
// each update start with considering things in valid state. // each update start with considering things in valid state.
state = PlaybackState.Valid; state = PlaybackState.Valid;
// our goal is to catch up to the time provided by the parent clock.
var proposedTime = parentGameplayClock.CurrentTime;
if (FrameStablePlayback) if (FrameStablePlayback)
// if we require frame stability, the proposed time will be adjusted to move at most one known // if we require frame stability, the proposed time will be adjusted to move at most one known
// frame interval in the current direction. // frame interval in the current direction.