2021-04-15 15:33:59 +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.
|
|
|
|
|
2022-06-17 15:37:17 +08:00
|
|
|
#nullable disable
|
|
|
|
|
2021-04-15 15:33:59 +08:00
|
|
|
using System;
|
2022-08-22 18:14:06 +08:00
|
|
|
using System.Collections.Generic;
|
2021-04-15 15:33:59 +08:00
|
|
|
using NUnit.Framework;
|
2022-08-22 18:14:06 +08:00
|
|
|
using osu.Framework.Graphics;
|
2021-04-15 15:33:59 +08:00
|
|
|
using osu.Framework.Testing;
|
|
|
|
using osu.Framework.Timing;
|
2021-05-03 12:38:53 +08:00
|
|
|
using osu.Game.Screens.OnlinePlay.Multiplayer.Spectate;
|
2022-08-22 18:14:06 +08:00
|
|
|
using osu.Game.Screens.Play;
|
2021-04-15 15:33:59 +08:00
|
|
|
using osu.Game.Tests.Visual;
|
|
|
|
|
|
|
|
namespace osu.Game.Tests.OnlinePlay
|
|
|
|
{
|
|
|
|
[HeadlessTest]
|
2021-04-23 18:12:30 +08:00
|
|
|
public class TestSceneCatchUpSyncManager : OsuTestScene
|
2021-04-15 15:33:59 +08:00
|
|
|
{
|
2022-08-22 18:14:06 +08:00
|
|
|
private GameplayClockContainer master;
|
2021-04-16 21:47:52 +08:00
|
|
|
private CatchUpSyncManager syncManager;
|
2021-04-15 15:33:59 +08:00
|
|
|
|
2022-08-22 18:14:06 +08:00
|
|
|
private Dictionary<ISpectatorPlayerClock, int> clocksById;
|
|
|
|
private ISpectatorPlayerClock player1;
|
|
|
|
private ISpectatorPlayerClock player2;
|
2021-04-15 15:33:59 +08:00
|
|
|
|
|
|
|
[SetUp]
|
|
|
|
public void Setup()
|
|
|
|
{
|
2022-08-22 18:14:06 +08:00
|
|
|
syncManager = new CatchUpSyncManager(master = new GameplayClockContainer(new TestManualClock()));
|
2022-08-23 12:52:43 +08:00
|
|
|
player1 = syncManager.CreateManagedClock();
|
|
|
|
player2 = syncManager.CreateManagedClock();
|
2021-04-15 15:33:59 +08:00
|
|
|
|
2022-08-22 18:14:06 +08:00
|
|
|
clocksById = new Dictionary<ISpectatorPlayerClock, int>
|
|
|
|
{
|
|
|
|
{ player1, 1 },
|
|
|
|
{ player2, 2 }
|
|
|
|
};
|
|
|
|
|
|
|
|
Schedule(() =>
|
|
|
|
{
|
|
|
|
Children = new Drawable[]
|
|
|
|
{
|
|
|
|
syncManager,
|
|
|
|
master
|
|
|
|
};
|
|
|
|
});
|
2021-04-15 15:33:59 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
[Test]
|
2021-06-11 16:59:27 +08:00
|
|
|
public void TestPlayerClocksStartWhenAllHaveFrames()
|
2021-04-15 15:33:59 +08:00
|
|
|
{
|
2021-04-26 16:19:44 +08:00
|
|
|
setWaiting(() => player1, false);
|
|
|
|
assertPlayerClockState(() => player1, false);
|
|
|
|
assertPlayerClockState(() => player2, false);
|
2021-04-15 15:33:59 +08:00
|
|
|
|
2021-04-26 16:19:44 +08:00
|
|
|
setWaiting(() => player2, false);
|
|
|
|
assertPlayerClockState(() => player1, true);
|
|
|
|
assertPlayerClockState(() => player2, true);
|
2021-04-15 15:33:59 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
[Test]
|
2021-06-11 16:59:27 +08:00
|
|
|
public void TestReadyPlayersStartWhenReadyForMaximumDelayTime()
|
2021-04-15 15:33:59 +08:00
|
|
|
{
|
2021-04-26 16:19:44 +08:00
|
|
|
setWaiting(() => player1, false);
|
2021-04-16 21:47:52 +08:00
|
|
|
AddWaitStep($"wait {CatchUpSyncManager.MAXIMUM_START_DELAY} milliseconds", (int)Math.Ceiling(CatchUpSyncManager.MAXIMUM_START_DELAY / TimePerAction));
|
2021-06-11 16:59:27 +08:00
|
|
|
assertPlayerClockState(() => player1, true);
|
|
|
|
assertPlayerClockState(() => player2, false);
|
2021-04-15 15:33:59 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
[Test]
|
2021-04-26 16:19:44 +08:00
|
|
|
public void TestPlayerClockDoesNotCatchUpWhenSlightlyOutOfSync()
|
2021-04-15 15:33:59 +08:00
|
|
|
{
|
|
|
|
setAllWaiting(false);
|
|
|
|
|
2021-04-16 21:47:52 +08:00
|
|
|
setMasterTime(CatchUpSyncManager.SYNC_TARGET + 1);
|
2021-04-26 16:19:44 +08:00
|
|
|
assertCatchingUp(() => player1, false);
|
2021-04-15 15:33:59 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
[Test]
|
2021-04-26 16:19:44 +08:00
|
|
|
public void TestPlayerClockStartsCatchingUpWhenTooFarBehind()
|
2021-04-15 15:33:59 +08:00
|
|
|
{
|
|
|
|
setAllWaiting(false);
|
|
|
|
|
2021-04-16 21:47:52 +08:00
|
|
|
setMasterTime(CatchUpSyncManager.MAX_SYNC_OFFSET + 1);
|
2021-04-26 16:19:44 +08:00
|
|
|
assertCatchingUp(() => player1, true);
|
|
|
|
assertCatchingUp(() => player2, true);
|
2021-04-15 15:33:59 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
[Test]
|
2021-04-26 16:19:44 +08:00
|
|
|
public void TestPlayerClockKeepsCatchingUpWhenSlightlyOutOfSync()
|
2021-04-15 15:33:59 +08:00
|
|
|
{
|
|
|
|
setAllWaiting(false);
|
|
|
|
|
2021-04-16 21:47:52 +08:00
|
|
|
setMasterTime(CatchUpSyncManager.MAX_SYNC_OFFSET + 1);
|
2021-04-26 16:19:44 +08:00
|
|
|
setPlayerClockTime(() => player1, CatchUpSyncManager.SYNC_TARGET + 1);
|
|
|
|
assertCatchingUp(() => player1, true);
|
2021-04-15 15:33:59 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
[Test]
|
2021-04-26 16:19:44 +08:00
|
|
|
public void TestPlayerClockStopsCatchingUpWhenInSync()
|
2021-04-15 15:33:59 +08:00
|
|
|
{
|
|
|
|
setAllWaiting(false);
|
|
|
|
|
2021-04-16 21:47:52 +08:00
|
|
|
setMasterTime(CatchUpSyncManager.MAX_SYNC_OFFSET + 2);
|
2021-04-26 16:19:44 +08:00
|
|
|
setPlayerClockTime(() => player1, CatchUpSyncManager.SYNC_TARGET);
|
|
|
|
assertCatchingUp(() => player1, false);
|
|
|
|
assertCatchingUp(() => player2, true);
|
2021-04-15 15:33:59 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
[Test]
|
2021-04-26 16:19:44 +08:00
|
|
|
public void TestPlayerClockDoesNotStopWhenSlightlyAhead()
|
2021-04-15 15:33:59 +08:00
|
|
|
{
|
|
|
|
setAllWaiting(false);
|
|
|
|
|
2021-04-26 16:19:44 +08:00
|
|
|
setPlayerClockTime(() => player1, -CatchUpSyncManager.SYNC_TARGET);
|
|
|
|
assertCatchingUp(() => player1, false);
|
|
|
|
assertPlayerClockState(() => player1, true);
|
2021-04-15 15:33:59 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
[Test]
|
2021-04-26 16:19:44 +08:00
|
|
|
public void TestPlayerClockStopsWhenTooFarAheadAndStartsWhenBackInSync()
|
2021-04-15 15:33:59 +08:00
|
|
|
{
|
|
|
|
setAllWaiting(false);
|
|
|
|
|
2021-04-26 16:19:44 +08:00
|
|
|
setPlayerClockTime(() => player1, -CatchUpSyncManager.SYNC_TARGET - 1);
|
2021-04-15 15:33:59 +08:00
|
|
|
|
|
|
|
// This is a silent catchup, where IsCatchingUp = false but IsRunning = false also.
|
2021-04-26 16:19:44 +08:00
|
|
|
assertCatchingUp(() => player1, false);
|
|
|
|
assertPlayerClockState(() => player1, false);
|
2021-04-15 15:33:59 +08:00
|
|
|
|
|
|
|
setMasterTime(1);
|
2021-04-26 16:19:44 +08:00
|
|
|
assertCatchingUp(() => player1, false);
|
|
|
|
assertPlayerClockState(() => player1, true);
|
2021-04-15 15:33:59 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
[Test]
|
2021-04-26 16:19:44 +08:00
|
|
|
public void TestInSyncPlayerClockDoesNotStartIfWaitingOnFrames()
|
2021-04-15 15:33:59 +08:00
|
|
|
{
|
|
|
|
setAllWaiting(false);
|
|
|
|
|
2021-04-26 16:19:44 +08:00
|
|
|
assertPlayerClockState(() => player1, true);
|
|
|
|
setWaiting(() => player1, true);
|
|
|
|
assertPlayerClockState(() => player1, false);
|
2021-04-15 15:33:59 +08:00
|
|
|
}
|
|
|
|
|
2022-08-22 18:14:06 +08:00
|
|
|
private void setWaiting(Func<ISpectatorPlayerClock> playerClock, bool waiting)
|
|
|
|
=> AddStep($"set player clock {clocksById[playerClock()]} waiting = {waiting}", () => playerClock().WaitingOnFrames.Value = waiting);
|
2021-04-15 15:33:59 +08:00
|
|
|
|
2021-04-26 16:19:44 +08:00
|
|
|
private void setAllWaiting(bool waiting) => AddStep($"set all player clocks waiting = {waiting}", () =>
|
2021-04-15 15:33:59 +08:00
|
|
|
{
|
2021-04-26 16:19:44 +08:00
|
|
|
player1.WaitingOnFrames.Value = waiting;
|
|
|
|
player2.WaitingOnFrames.Value = waiting;
|
2021-04-15 15:33:59 +08:00
|
|
|
});
|
|
|
|
|
|
|
|
private void setMasterTime(double time)
|
|
|
|
=> AddStep($"set master = {time}", () => master.Seek(time));
|
|
|
|
|
|
|
|
/// <summary>
|
2021-04-26 16:19:44 +08:00
|
|
|
/// clock.Time = master.Time - offsetFromMaster
|
2021-04-15 15:33:59 +08:00
|
|
|
/// </summary>
|
2022-08-22 18:14:06 +08:00
|
|
|
private void setPlayerClockTime(Func<ISpectatorPlayerClock> playerClock, double offsetFromMaster)
|
|
|
|
=> AddStep($"set player clock {clocksById[playerClock()]} = master - {offsetFromMaster}", () => playerClock().Seek(master.CurrentTime - offsetFromMaster));
|
2021-04-26 16:30:27 +08:00
|
|
|
|
2022-08-22 18:14:06 +08:00
|
|
|
private void assertCatchingUp(Func<ISpectatorPlayerClock> playerClock, bool catchingUp) =>
|
|
|
|
AddAssert($"player clock {clocksById[playerClock()]} {(catchingUp ? "is" : "is not")} catching up", () => playerClock().IsCatchingUp == catchingUp);
|
2021-04-15 15:33:59 +08:00
|
|
|
|
2022-08-22 18:14:06 +08:00
|
|
|
private void assertPlayerClockState(Func<ISpectatorPlayerClock> playerClock, bool running)
|
|
|
|
=> AddAssert($"player clock {clocksById[playerClock()]} {(running ? "is" : "is not")} running", () => playerClock().IsRunning == running);
|
2021-04-15 15:33:59 +08:00
|
|
|
|
|
|
|
private class TestManualClock : ManualClock, IAdjustableClock
|
|
|
|
{
|
2021-06-11 16:59:27 +08:00
|
|
|
public TestManualClock()
|
|
|
|
{
|
|
|
|
IsRunning = true;
|
|
|
|
}
|
|
|
|
|
2021-04-15 15:33:59 +08:00
|
|
|
public void Start() => IsRunning = true;
|
|
|
|
|
|
|
|
public void Stop() => IsRunning = false;
|
|
|
|
|
|
|
|
public bool Seek(double position)
|
|
|
|
{
|
|
|
|
CurrentTime = position;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void Reset()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
public void ResetSpeedAdjustments()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|