1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-15 00:53:22 +08:00
osu-lazer/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs

312 lines
12 KiB
C#
Raw Normal View History

2021-04-08 21:14:30 +08:00
// 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.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
2021-06-11 17:13:54 +08:00
using osu.Game.Rulesets.UI;
2021-04-08 21:14:30 +08:00
using osu.Game.Screens.OnlinePlay.Multiplayer.Spectate;
using osu.Game.Screens.Play;
using osu.Game.Tests.Beatmaps.IO;
using osu.Game.Users;
2021-04-08 21:14:30 +08:00
namespace osu.Game.Tests.Visual.Multiplayer
{
2021-04-23 18:23:52 +08:00
public class TestSceneMultiSpectatorScreen : MultiplayerTestScene
2021-04-08 21:14:30 +08:00
{
[Resolved]
private OsuGameBase game { get; set; }
[Resolved]
private BeatmapManager beatmapManager { get; set; }
2021-04-22 22:39:02 +08:00
private MultiSpectatorScreen spectatorScreen;
2021-04-08 21:14:30 +08:00
private readonly List<int> playingUserIds = new List<int>();
private BeatmapSetInfo importedSet;
private BeatmapInfo importedBeatmap;
private int importedBeatmapId;
[BackgroundDependencyLoader]
private void load()
{
importedSet = ImportBeatmapTest.LoadOszIntoOsu(game, virtualTrack: true).Result;
importedBeatmap = importedSet.Beatmaps.First(b => b.RulesetID == 0);
importedBeatmapId = importedBeatmap.OnlineBeatmapID ?? -1;
}
[SetUp]
public new void Setup() => Schedule(() => playingUserIds.Clear());
2021-04-08 21:14:30 +08:00
[Test]
public void TestDelayedStart()
{
AddStep("start players silently", () =>
{
OnlinePlayDependencies.Client.AddUser(new User { Id = PLAYER_1_ID }, true);
OnlinePlayDependencies.Client.AddUser(new User { Id = PLAYER_2_ID }, true);
playingUserIds.Add(PLAYER_1_ID);
playingUserIds.Add(PLAYER_2_ID);
});
loadSpectateScreen(false);
AddWaitStep("wait a bit", 10);
AddStep("load player first_player_id", () => SpectatorClient.StartPlay(PLAYER_1_ID, importedBeatmapId));
2021-04-22 22:39:02 +08:00
AddUntilStep("one player added", () => spectatorScreen.ChildrenOfType<Player>().Count() == 1);
AddWaitStep("wait a bit", 10);
AddStep("load player second_player_id", () => SpectatorClient.StartPlay(PLAYER_2_ID, importedBeatmapId));
2021-04-22 22:39:02 +08:00
AddUntilStep("two players added", () => spectatorScreen.ChildrenOfType<Player>().Count() == 2);
}
2021-04-08 21:14:30 +08:00
[Test]
public void TestGeneral()
{
int[] userIds = Enumerable.Range(0, 4).Select(i => PLAYER_1_ID + i).ToArray();
2021-04-08 21:14:30 +08:00
start(userIds);
loadSpectateScreen();
sendFrames(userIds, 1000);
AddWaitStep("wait a bit", 20);
}
2021-06-11 18:15:53 +08:00
[Test]
public void TestTimeDoesNotProgressWhileAllPlayersPaused()
{
start(new[] { PLAYER_1_ID, PLAYER_2_ID });
loadSpectateScreen();
sendFrames(PLAYER_1_ID, 40);
sendFrames(PLAYER_2_ID, 20);
2021-06-11 18:15:53 +08:00
checkPaused(PLAYER_2_ID, true);
checkPausedInstant(PLAYER_1_ID, false);
AddAssert("master clock still running", () => this.ChildrenOfType<MasterGameplayClockContainer>().Single().IsRunning);
checkPaused(PLAYER_1_ID, true);
AddUntilStep("master clock paused", () => !this.ChildrenOfType<MasterGameplayClockContainer>().Single().IsRunning);
}
2021-04-08 21:14:30 +08:00
[Test]
public void TestPlayersMustStartSimultaneously()
{
start(new[] { PLAYER_1_ID, PLAYER_2_ID });
2021-04-08 21:14:30 +08:00
loadSpectateScreen();
// Send frames for one player only, both should remain paused.
sendFrames(PLAYER_1_ID, 20);
checkPausedInstant(PLAYER_1_ID, true);
checkPausedInstant(PLAYER_2_ID, true);
2021-04-08 21:14:30 +08:00
// Send frames for the other player, both should now start playing.
sendFrames(PLAYER_2_ID, 20);
checkPausedInstant(PLAYER_1_ID, false);
checkPausedInstant(PLAYER_2_ID, false);
2021-04-08 21:14:30 +08:00
}
[Test]
public void TestPlayersDoNotStartSimultaneouslyIfBufferingForMaximumStartDelay()
2021-04-08 21:14:30 +08:00
{
start(new[] { PLAYER_1_ID, PLAYER_2_ID });
2021-04-08 21:14:30 +08:00
loadSpectateScreen();
// Send frames for one player only, both should remain paused.
sendFrames(PLAYER_1_ID, 1000);
checkPausedInstant(PLAYER_1_ID, true);
checkPausedInstant(PLAYER_2_ID, true);
2021-04-08 21:14:30 +08:00
// Wait for the start delay seconds...
AddWaitStep("wait maximum start delay seconds", (int)(CatchUpSyncManager.MAXIMUM_START_DELAY / TimePerAction));
2021-04-08 21:14:30 +08:00
// Player 1 should start playing by itself, player 2 should remain paused.
checkPausedInstant(PLAYER_1_ID, false);
checkPausedInstant(PLAYER_2_ID, true);
2021-04-08 21:14:30 +08:00
}
[Test]
public void TestPlayersContinueWhileOthersBuffer()
{
start(new[] { PLAYER_1_ID, PLAYER_2_ID });
2021-04-08 21:14:30 +08:00
loadSpectateScreen();
// Send initial frames for both players. A few more for player 1.
sendFrames(PLAYER_1_ID, 20);
2021-07-05 23:52:39 +08:00
sendFrames(PLAYER_2_ID);
checkPausedInstant(PLAYER_1_ID, false);
checkPausedInstant(PLAYER_2_ID, false);
2021-04-08 21:14:30 +08:00
// Eventually player 2 will pause, player 1 must remain running.
checkPaused(PLAYER_2_ID, true);
checkPausedInstant(PLAYER_1_ID, false);
2021-04-08 21:14:30 +08:00
// Eventually both players will run out of frames and should pause.
checkPaused(PLAYER_1_ID, true);
checkPausedInstant(PLAYER_2_ID, true);
2021-04-08 21:14:30 +08:00
// Send more frames for the first player only. Player 1 should start playing with player 2 remaining paused.
sendFrames(PLAYER_1_ID, 20);
checkPausedInstant(PLAYER_2_ID, true);
checkPausedInstant(PLAYER_1_ID, false);
2021-04-08 21:14:30 +08:00
// Send more frames for the second player. Both should be playing
sendFrames(PLAYER_2_ID, 20);
checkPausedInstant(PLAYER_2_ID, false);
checkPausedInstant(PLAYER_1_ID, false);
2021-04-08 21:14:30 +08:00
}
[Test]
public void TestPlayersCatchUpAfterFallingBehind()
{
start(new[] { PLAYER_1_ID, PLAYER_2_ID });
2021-04-08 21:14:30 +08:00
loadSpectateScreen();
// Send initial frames for both players. A few more for player 1.
sendFrames(PLAYER_1_ID, 1000);
sendFrames(PLAYER_2_ID, 30);
checkPausedInstant(PLAYER_1_ID, false);
checkPausedInstant(PLAYER_2_ID, false);
2021-04-08 21:14:30 +08:00
// Eventually player 2 will run out of frames and should pause.
checkPaused(PLAYER_2_ID, true);
2021-04-08 21:14:30 +08:00
AddWaitStep("wait a few more frames", 10);
// Send more frames for player 2. It should unpause.
sendFrames(PLAYER_2_ID, 1000);
checkPausedInstant(PLAYER_2_ID, false);
2021-04-08 21:14:30 +08:00
// Player 2 should catch up to player 1 after unpausing.
waitForCatchup(PLAYER_2_ID);
2021-04-13 21:40:10 +08:00
AddWaitStep("wait a bit", 10);
2021-04-08 21:14:30 +08:00
}
[Test]
public void TestMostInSyncUserIsAudioSource()
{
start(new[] { PLAYER_1_ID, PLAYER_2_ID });
loadSpectateScreen();
2021-04-26 18:01:30 +08:00
assertMuted(PLAYER_1_ID, true);
assertMuted(PLAYER_2_ID, true);
2021-07-05 23:52:39 +08:00
sendFrames(PLAYER_1_ID);
sendFrames(PLAYER_2_ID, 20);
checkPaused(PLAYER_1_ID, false);
assertOneNotMuted();
checkPaused(PLAYER_1_ID, true);
2021-04-26 18:01:30 +08:00
assertMuted(PLAYER_1_ID, true);
assertMuted(PLAYER_2_ID, false);
sendFrames(PLAYER_1_ID, 100);
waitForCatchup(PLAYER_1_ID);
checkPaused(PLAYER_2_ID, true);
2021-04-26 18:01:30 +08:00
assertMuted(PLAYER_1_ID, false);
assertMuted(PLAYER_2_ID, true);
sendFrames(PLAYER_2_ID, 100);
waitForCatchup(PLAYER_2_ID);
2021-04-26 18:01:30 +08:00
assertMuted(PLAYER_1_ID, false);
assertMuted(PLAYER_2_ID, true);
}
2021-06-11 17:13:54 +08:00
[Test]
public void TestSpectatingDuringGameplay()
{
var players = new[] { PLAYER_1_ID, PLAYER_2_ID };
start(players);
sendFrames(players, 300);
loadSpectateScreen();
sendFrames(players, 300);
AddUntilStep("playing from correct point in time", () => this.ChildrenOfType<DrawableRuleset>().All(r => r.FrameStableClock.CurrentTime > 30000));
}
[Test]
public void TestSpectatingDuringGameplayWithLateFrames()
{
start(new[] { PLAYER_1_ID, PLAYER_2_ID });
sendFrames(new[] { PLAYER_1_ID, PLAYER_2_ID }, 300);
loadSpectateScreen();
sendFrames(PLAYER_1_ID, 300);
AddWaitStep("wait maximum start delay seconds", (int)(CatchUpSyncManager.MAXIMUM_START_DELAY / TimePerAction));
checkPaused(PLAYER_1_ID, false);
sendFrames(PLAYER_2_ID, 300);
AddUntilStep("player 2 playing from correct point in time", () => getPlayer(PLAYER_2_ID).ChildrenOfType<DrawableRuleset>().Single().FrameStableClock.CurrentTime > 30000);
}
private void loadSpectateScreen(bool waitForPlayerLoad = true)
2021-04-08 21:14:30 +08:00
{
AddStep("load screen", () =>
{
Beatmap.Value = beatmapManager.GetWorkingBeatmap(importedBeatmap);
Ruleset.Value = importedBeatmap.Ruleset;
2021-04-22 22:39:02 +08:00
LoadScreen(spectatorScreen = new MultiSpectatorScreen(playingUserIds.ToArray()));
2021-04-08 21:14:30 +08:00
});
2021-04-22 22:39:02 +08:00
AddUntilStep("wait for screen load", () => spectatorScreen.LoadState == LoadState.Loaded && (!waitForPlayerLoad || spectatorScreen.AllPlayersLoaded));
2021-04-08 21:14:30 +08:00
}
private void start(int[] userIds, int? beatmapId = null)
{
AddStep("start play", () =>
{
foreach (int id in userIds)
{
OnlinePlayDependencies.Client.AddUser(new User { Id = id }, true);
SpectatorClient.StartPlay(id, beatmapId ?? importedBeatmapId);
2021-04-08 21:14:30 +08:00
playingUserIds.Add(id);
}
});
}
private void sendFrames(int userId, int count = 10) => sendFrames(new[] { userId }, count);
private void sendFrames(int[] userIds, int count = 10)
{
AddStep("send frames", () =>
{
foreach (int id in userIds)
SpectatorClient.SendFrames(id, count);
2021-04-08 21:14:30 +08:00
});
}
private void checkPaused(int userId, bool state)
=> AddUntilStep($"{userId} is {(state ? "paused" : "playing")}", () => getPlayer(userId).ChildrenOfType<GameplayClockContainer>().First().GameplayClock.IsRunning != state);
private void checkPausedInstant(int userId, bool state)
{
checkPaused(userId, state);
// Todo: The following should work, but is broken because SpectatorScreen retrieves the WorkingBeatmap via the BeatmapManager, bypassing the test scene clock and running real-time.
// AddAssert($"{userId} is {(state ? "paused" : "playing")}", () => getPlayer(userId).ChildrenOfType<GameplayClockContainer>().First().GameplayClock.IsRunning != state);
}
private void assertOneNotMuted() => AddAssert("one player not muted", () => spectatorScreen.ChildrenOfType<PlayerArea>().Count(p => !p.Mute) == 1);
2021-04-26 18:01:30 +08:00
private void assertMuted(int userId, bool muted)
=> AddAssert($"{userId} {(muted ? "is" : "is not")} muted", () => getInstance(userId).Mute == muted);
2021-04-08 21:14:30 +08:00
private void waitForCatchup(int userId)
=> AddUntilStep($"{userId} not catching up", () => !getInstance(userId).GameplayClock.IsCatchingUp);
2021-04-08 21:14:30 +08:00
private Player getPlayer(int userId) => getInstance(userId).ChildrenOfType<Player>().Single();
2021-04-22 22:52:22 +08:00
private PlayerArea getInstance(int userId) => spectatorScreen.ChildrenOfType<PlayerArea>().Single(p => p.UserId == userId);
2021-04-08 21:14:30 +08:00
}
}