1
0
mirror of https://github.com/ppy/osu.git synced 2024-11-11 09:27:29 +08:00

Merge pull request #18353 from frenzibyte/spectator-catch-up-work-alt

Disallow multi-spectator player clocks from being started/stopped externally
This commit is contained in:
Dan Balasescu 2022-05-23 13:59:54 +09:00 committed by GitHub
commit 7d0470794b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 46 additions and 22 deletions

View File

@ -1,6 +1,7 @@
// 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.Linq;
using NUnit.Framework;
@ -15,13 +16,14 @@ using osu.Game.Configuration;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Online.Multiplayer;
using osu.Game.Online.Multiplayer.MatchTypes.TeamVersus;
using osu.Game.Online.Rooms;
using osu.Game.Rulesets.UI;
using osu.Game.Screens.OnlinePlay.Multiplayer.Spectate;
using osu.Game.Screens.Play;
using osu.Game.Screens.Play.HUD;
using osu.Game.Screens.Play.PlayerSettings;
using osu.Game.Storyboards;
using osu.Game.Tests.Beatmaps.IO;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Tests.Visual.Multiplayer
@ -349,15 +351,27 @@ namespace osu.Game.Tests.Visual.Multiplayer
}
/// <summary>
/// Tests spectating with a gameplay start time set to a negative value.
/// Simulating beatmaps with high <see cref="BeatmapInfo.AudioLeadIn"/> or negative time storyboard elements.
/// Tests spectating with a beatmap that has a high <see cref="BeatmapInfo.AudioLeadIn"/> value.
/// </summary>
[Test]
public void TestNegativeGameplayStartTime()
public void TestAudioLeadIn() => testLeadIn(b => b.BeatmapInfo.AudioLeadIn = 2000);
/// <summary>
/// Tests spectating with a beatmap that has a storyboard element with a negative start time (i.e. intro storyboard element).
/// </summary>
[Test]
public void TestIntroStoryboardElement() => testLeadIn(b =>
{
var sprite = new StoryboardSprite("unknown", Anchor.TopLeft, Vector2.Zero);
sprite.TimelineGroup.Alpha.Add(Easing.None, -2000, 0, 0, 1);
b.Storyboard.GetLayer("Background").Add(sprite);
});
private void testLeadIn(Action<WorkingBeatmap> applyToBeatmap = null)
{
start(PLAYER_1_ID);
loadSpectateScreen(false, -500);
loadSpectateScreen(false, applyToBeatmap);
// to ensure negative gameplay start time does not affect spectator, send frames exactly after StartGameplay().
// (similar to real spectating sessions in which the first frames get sent between StartGameplay() and player load complete)
@ -371,14 +385,16 @@ namespace osu.Game.Tests.Visual.Multiplayer
assertRunning(PLAYER_1_ID);
}
private void loadSpectateScreen(bool waitForPlayerLoad = true, double? gameplayStartTime = null)
private void loadSpectateScreen(bool waitForPlayerLoad = true, Action<WorkingBeatmap> applyToBeatmap = null)
{
AddStep(!gameplayStartTime.HasValue ? "load screen" : $"load screen (start = {gameplayStartTime}ms)", () =>
AddStep("load screen", () =>
{
Beatmap.Value = beatmapManager.GetWorkingBeatmap(importedBeatmap);
Ruleset.Value = importedBeatmap.Ruleset;
LoadScreen(spectatorScreen = new TestMultiSpectatorScreen(SelectedRoom.Value, playingUsers.ToArray(), gameplayStartTime));
applyToBeatmap?.Invoke(Beatmap.Value);
LoadScreen(spectatorScreen = new MultiSpectatorScreen(SelectedRoom.Value, playingUsers.ToArray()));
});
AddUntilStep("wait for screen load", () => spectatorScreen.LoadState == LoadState.Loaded && (!waitForPlayerLoad || spectatorScreen.AllPlayersLoaded));
@ -461,19 +477,5 @@ namespace osu.Game.Tests.Visual.Multiplayer
private GameplayLeaderboardScore getLeaderboardScore(int userId) => spectatorScreen.ChildrenOfType<GameplayLeaderboardScore>().Single(s => s.User?.Id == userId);
private int[] getPlayerIds(int count) => Enumerable.Range(PLAYER_1_ID, count).ToArray();
private class TestMultiSpectatorScreen : MultiSpectatorScreen
{
private readonly double? startTime;
public TestMultiSpectatorScreen(Room room, MultiplayerRoomUser[] users, double? startTime = null)
: base(room, users)
{
this.startTime = startTime;
}
protected override MasterGameplayClockContainer CreateMasterGameplayClockContainer(WorkingBeatmap beatmap)
=> new MasterGameplayClockContainer(beatmap, 0) { StartTime = startTime ?? 0 };
}
}
}

View File

@ -34,6 +34,16 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
public void Stop() => IsRunning = false;
void IAdjustableClock.Start()
{
// Our running state should only be managed by an ISyncManager, ignore calls from external sources.
}
void IAdjustableClock.Stop()
{
// Our running state should only be managed by an ISyncManager, ignore calls from external sources.
}
public bool Seek(double position)
{
CurrentTime = position;

View File

@ -144,6 +144,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
// Make sure the player clock is running if it can.
if (!clock.WaitingOnFrames.Value)
clock.Start();
else
clock.Stop();
if (clock.IsCatchingUp)
{

View File

@ -11,6 +11,16 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
/// </summary>
public interface ISpectatorPlayerClock : IFrameBasedClock, IAdjustableClock
{
/// <summary>
/// Starts this <see cref="ISpectatorPlayerClock"/>.
/// </summary>
new void Start();
/// <summary>
/// Stops this <see cref="ISpectatorPlayerClock"/>.
/// </summary>
new void Stop();
/// <summary>
/// Whether this clock is waiting on frames to continue playback.
/// </summary>