mirror of
https://github.com/ppy/osu.git
synced 2024-11-11 09:27:29 +08:00
Change terminology from "slave" to "player clock"
This commit is contained in:
parent
737a15c2d4
commit
5b4cb71cc7
@ -17,31 +17,31 @@ namespace osu.Game.Tests.OnlinePlay
|
||||
private TestManualClock master;
|
||||
private CatchUpSyncManager syncManager;
|
||||
|
||||
private TestSlaveClock slave1;
|
||||
private TestSlaveClock slave2;
|
||||
private TestSpectatorPlayerClock player1;
|
||||
private TestSpectatorPlayerClock player2;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
syncManager = new CatchUpSyncManager(master = new TestManualClock());
|
||||
syncManager.AddSlave(slave1 = new TestSlaveClock(1));
|
||||
syncManager.AddSlave(slave2 = new TestSlaveClock(2));
|
||||
syncManager.AddPlayerClock(player1 = new TestSpectatorPlayerClock(1));
|
||||
syncManager.AddPlayerClock(player2 = new TestSpectatorPlayerClock(2));
|
||||
|
||||
Schedule(() => Child = syncManager);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestMasterClockStartsWhenAllSlavesHaveFrames()
|
||||
public void TestMasterClockStartsWhenAllPlayerClocksHaveFrames()
|
||||
{
|
||||
setWaiting(() => slave1, false);
|
||||
setWaiting(() => player1, false);
|
||||
assertMasterState(false);
|
||||
assertSlaveState(() => slave1, false);
|
||||
assertSlaveState(() => slave2, false);
|
||||
assertPlayerClockState(() => player1, false);
|
||||
assertPlayerClockState(() => player2, false);
|
||||
|
||||
setWaiting(() => slave2, false);
|
||||
setWaiting(() => player2, false);
|
||||
assertMasterState(true);
|
||||
assertSlaveState(() => slave1, true);
|
||||
assertSlaveState(() => slave2, true);
|
||||
assertPlayerClockState(() => player1, true);
|
||||
assertPlayerClockState(() => player2, true);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -54,115 +54,115 @@ namespace osu.Game.Tests.OnlinePlay
|
||||
[Test]
|
||||
public void TestMasterClockStartsWhenAnyReadyForMaximumDelayTime()
|
||||
{
|
||||
setWaiting(() => slave1, false);
|
||||
setWaiting(() => player1, false);
|
||||
AddWaitStep($"wait {CatchUpSyncManager.MAXIMUM_START_DELAY} milliseconds", (int)Math.Ceiling(CatchUpSyncManager.MAXIMUM_START_DELAY / TimePerAction));
|
||||
assertMasterState(true);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSlaveDoesNotCatchUpWhenSlightlyOutOfSync()
|
||||
public void TestPlayerClockDoesNotCatchUpWhenSlightlyOutOfSync()
|
||||
{
|
||||
setAllWaiting(false);
|
||||
|
||||
setMasterTime(CatchUpSyncManager.SYNC_TARGET + 1);
|
||||
assertCatchingUp(() => slave1, false);
|
||||
assertCatchingUp(() => player1, false);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSlaveStartsCatchingUpWhenTooFarBehind()
|
||||
public void TestPlayerClockStartsCatchingUpWhenTooFarBehind()
|
||||
{
|
||||
setAllWaiting(false);
|
||||
|
||||
setMasterTime(CatchUpSyncManager.MAX_SYNC_OFFSET + 1);
|
||||
assertCatchingUp(() => slave1, true);
|
||||
assertCatchingUp(() => slave2, true);
|
||||
assertCatchingUp(() => player1, true);
|
||||
assertCatchingUp(() => player2, true);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSlaveKeepsCatchingUpWhenSlightlyOutOfSync()
|
||||
public void TestPlayerClockKeepsCatchingUpWhenSlightlyOutOfSync()
|
||||
{
|
||||
setAllWaiting(false);
|
||||
|
||||
setMasterTime(CatchUpSyncManager.MAX_SYNC_OFFSET + 1);
|
||||
setSlaveTime(() => slave1, CatchUpSyncManager.SYNC_TARGET + 1);
|
||||
assertCatchingUp(() => slave1, true);
|
||||
setPlayerClockTime(() => player1, CatchUpSyncManager.SYNC_TARGET + 1);
|
||||
assertCatchingUp(() => player1, true);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSlaveStopsCatchingUpWhenInSync()
|
||||
public void TestPlayerClockStopsCatchingUpWhenInSync()
|
||||
{
|
||||
setAllWaiting(false);
|
||||
|
||||
setMasterTime(CatchUpSyncManager.MAX_SYNC_OFFSET + 2);
|
||||
setSlaveTime(() => slave1, CatchUpSyncManager.SYNC_TARGET);
|
||||
assertCatchingUp(() => slave1, false);
|
||||
assertCatchingUp(() => slave2, true);
|
||||
setPlayerClockTime(() => player1, CatchUpSyncManager.SYNC_TARGET);
|
||||
assertCatchingUp(() => player1, false);
|
||||
assertCatchingUp(() => player2, true);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSlaveDoesNotStopWhenSlightlyAhead()
|
||||
public void TestPlayerClockDoesNotStopWhenSlightlyAhead()
|
||||
{
|
||||
setAllWaiting(false);
|
||||
|
||||
setSlaveTime(() => slave1, -CatchUpSyncManager.SYNC_TARGET);
|
||||
assertCatchingUp(() => slave1, false);
|
||||
assertSlaveState(() => slave1, true);
|
||||
setPlayerClockTime(() => player1, -CatchUpSyncManager.SYNC_TARGET);
|
||||
assertCatchingUp(() => player1, false);
|
||||
assertPlayerClockState(() => player1, true);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSlaveStopsWhenTooFarAheadAndStartsWhenBackInSync()
|
||||
public void TestPlayerClockStopsWhenTooFarAheadAndStartsWhenBackInSync()
|
||||
{
|
||||
setAllWaiting(false);
|
||||
|
||||
setSlaveTime(() => slave1, -CatchUpSyncManager.SYNC_TARGET - 1);
|
||||
setPlayerClockTime(() => player1, -CatchUpSyncManager.SYNC_TARGET - 1);
|
||||
|
||||
// This is a silent catchup, where IsCatchingUp = false but IsRunning = false also.
|
||||
assertCatchingUp(() => slave1, false);
|
||||
assertSlaveState(() => slave1, false);
|
||||
assertCatchingUp(() => player1, false);
|
||||
assertPlayerClockState(() => player1, false);
|
||||
|
||||
setMasterTime(1);
|
||||
assertCatchingUp(() => slave1, false);
|
||||
assertSlaveState(() => slave1, true);
|
||||
assertCatchingUp(() => player1, false);
|
||||
assertPlayerClockState(() => player1, true);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestInSyncSlaveDoesNotStartIfWaitingOnFrames()
|
||||
public void TestInSyncPlayerClockDoesNotStartIfWaitingOnFrames()
|
||||
{
|
||||
setAllWaiting(false);
|
||||
|
||||
assertSlaveState(() => slave1, true);
|
||||
setWaiting(() => slave1, true);
|
||||
assertSlaveState(() => slave1, false);
|
||||
assertPlayerClockState(() => player1, true);
|
||||
setWaiting(() => player1, true);
|
||||
assertPlayerClockState(() => player1, false);
|
||||
}
|
||||
|
||||
private void setWaiting(Func<TestSlaveClock> slave, bool waiting)
|
||||
=> AddStep($"set slave {slave().Id} waiting = {waiting}", () => slave().WaitingOnFrames.Value = waiting);
|
||||
private void setWaiting(Func<TestSpectatorPlayerClock> playerClock, bool waiting)
|
||||
=> AddStep($"set player clock {playerClock().Id} waiting = {waiting}", () => playerClock().WaitingOnFrames.Value = waiting);
|
||||
|
||||
private void setAllWaiting(bool waiting) => AddStep($"set all slaves waiting = {waiting}", () =>
|
||||
private void setAllWaiting(bool waiting) => AddStep($"set all player clocks waiting = {waiting}", () =>
|
||||
{
|
||||
slave1.WaitingOnFrames.Value = waiting;
|
||||
slave2.WaitingOnFrames.Value = waiting;
|
||||
player1.WaitingOnFrames.Value = waiting;
|
||||
player2.WaitingOnFrames.Value = waiting;
|
||||
});
|
||||
|
||||
private void setMasterTime(double time)
|
||||
=> AddStep($"set master = {time}", () => master.Seek(time));
|
||||
|
||||
/// <summary>
|
||||
/// slave.Time = master.Time - offsetFromMaster
|
||||
/// clock.Time = master.Time - offsetFromMaster
|
||||
/// </summary>
|
||||
private void setSlaveTime(Func<TestSlaveClock> slave, double offsetFromMaster)
|
||||
=> AddStep($"set slave {slave().Id} = master - {offsetFromMaster}", () => slave().Seek(master.CurrentTime - offsetFromMaster));
|
||||
private void setPlayerClockTime(Func<TestSpectatorPlayerClock> playerClock, double offsetFromMaster)
|
||||
=> AddStep($"set player clock {playerClock().Id} = master - {offsetFromMaster}", () => playerClock().Seek(master.CurrentTime - offsetFromMaster));
|
||||
|
||||
private void assertMasterState(bool running)
|
||||
=> AddAssert($"master clock {(running ? "is" : "is not")} running", () => master.IsRunning == running);
|
||||
|
||||
private void assertCatchingUp(Func<TestSlaveClock> slave, bool catchingUp) =>
|
||||
AddAssert($"slave {slave().Id} {(catchingUp ? "is" : "is not")} catching up", () => slave().IsCatchingUp == catchingUp);
|
||||
private void assertCatchingUp(Func<TestSpectatorPlayerClock> playerClock, bool catchingUp) =>
|
||||
AddAssert($"player clock {playerClock().Id} {(catchingUp ? "is" : "is not")} catching up", () => playerClock().IsCatchingUp == catchingUp);
|
||||
|
||||
private void assertSlaveState(Func<TestSlaveClock> slave, bool running)
|
||||
=> AddAssert($"slave {slave().Id} {(running ? "is" : "is not")} running", () => slave().IsRunning == running);
|
||||
private void assertPlayerClockState(Func<TestSpectatorPlayerClock> playerClock, bool running)
|
||||
=> AddAssert($"player clock {playerClock().Id} {(running ? "is" : "is not")} running", () => playerClock().IsRunning == running);
|
||||
|
||||
private class TestSlaveClock : TestManualClock, ISlaveClock
|
||||
private class TestSpectatorPlayerClock : TestManualClock, ISpectatorPlayerClock
|
||||
{
|
||||
public Bindable<bool> WaitingOnFrames { get; } = new Bindable<bool>(true);
|
||||
|
||||
@ -170,7 +170,7 @@ namespace osu.Game.Tests.OnlinePlay
|
||||
|
||||
public readonly int Id;
|
||||
|
||||
public TestSlaveClock(int id)
|
||||
public TestSpectatorPlayerClock(int id)
|
||||
{
|
||||
Id = id;
|
||||
|
||||
|
@ -19,24 +19,24 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
|
||||
{
|
||||
private readonly Bindable<bool> waitingOnFrames = new Bindable<bool>(true);
|
||||
private readonly Score score;
|
||||
private readonly ISlaveClock slaveClock;
|
||||
private readonly ISpectatorPlayerClock spectatorPlayerClock;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="MultiSpectatorPlayer"/>.
|
||||
/// </summary>
|
||||
/// <param name="score">The score containing the player's replay.</param>
|
||||
/// <param name="slaveClock">The clock controlling the gameplay running state.</param>
|
||||
public MultiSpectatorPlayer([NotNull] Score score, [NotNull] ISlaveClock slaveClock)
|
||||
/// <param name="spectatorPlayerClock">The clock controlling the gameplay running state.</param>
|
||||
public MultiSpectatorPlayer([NotNull] Score score, [NotNull] ISpectatorPlayerClock spectatorPlayerClock)
|
||||
: base(score)
|
||||
{
|
||||
this.score = score;
|
||||
this.slaveClock = slaveClock;
|
||||
this.spectatorPlayerClock = spectatorPlayerClock;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
slaveClock.WaitingOnFrames.BindTo(waitingOnFrames);
|
||||
spectatorPlayerClock.WaitingOnFrames.BindTo(waitingOnFrames);
|
||||
}
|
||||
|
||||
protected override void UpdateAfterChildren()
|
||||
@ -48,18 +48,18 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
|
||||
}
|
||||
|
||||
protected override GameplayClockContainer CreateGameplayClockContainer(WorkingBeatmap beatmap, double gameplayStart)
|
||||
=> new SlaveGameplayClockContainer(slaveClock);
|
||||
=> new SpectatorGameplayClockContainer(spectatorPlayerClock);
|
||||
|
||||
private class SlaveGameplayClockContainer : GameplayClockContainer
|
||||
private class SpectatorGameplayClockContainer : GameplayClockContainer
|
||||
{
|
||||
public SlaveGameplayClockContainer([NotNull] IClock sourceClock)
|
||||
public SpectatorGameplayClockContainer([NotNull] IClock sourceClock)
|
||||
: base(sourceClock)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
// The slave clock's running state is controlled externally, but the local pausing state needs to be updated to stop gameplay.
|
||||
// The player clock's running state is controlled externally, but the local pausing state needs to be updated to stop gameplay.
|
||||
if (SourceClock.IsRunning)
|
||||
Start();
|
||||
else
|
||||
|
@ -79,7 +79,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
|
||||
};
|
||||
|
||||
for (int i = 0; i < UserIds.Length; i++)
|
||||
grid.Add(instances[i] = new PlayerArea(UserIds[i], new CatchUpSlaveClock(masterClockContainer.GameplayClock)));
|
||||
grid.Add(instances[i] = new PlayerArea(UserIds[i], new CatchUpSpectatorPlayerClock(masterClockContainer.GameplayClock)));
|
||||
|
||||
// Todo: This is not quite correct - it should be per-user to adjust for other mod combinations.
|
||||
var playableBeatmap = Beatmap.Value.GetPlayableBeatmap(Ruleset.Value);
|
||||
@ -104,7 +104,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
|
||||
if (!isCandidateAudioSource(currentAudioSource?.GameplayClock))
|
||||
{
|
||||
currentAudioSource = instances.Where(i => isCandidateAudioSource(i.GameplayClock))
|
||||
.OrderBy(i => Math.Abs(i.GameplayClock.CurrentTime - syncManager.Master.CurrentTime))
|
||||
.OrderBy(i => Math.Abs(i.GameplayClock.CurrentTime - syncManager.MasterClock.CurrentTime))
|
||||
.FirstOrDefault();
|
||||
|
||||
foreach (var instance in instances)
|
||||
@ -112,7 +112,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
|
||||
}
|
||||
}
|
||||
|
||||
private bool isCandidateAudioSource([CanBeNull] ISlaveClock clock)
|
||||
private bool isCandidateAudioSource([CanBeNull] ISpectatorPlayerClock clock)
|
||||
=> clock?.IsRunning == true && !clock.IsCatchingUp && !clock.WaitingOnFrames.Value;
|
||||
|
||||
protected override void OnUserStateChanged(int userId, SpectatorState spectatorState)
|
||||
@ -124,7 +124,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
|
||||
var instance = instances[getIndexForUser(userId)];
|
||||
instance.LoadScore(gameplayState.Score);
|
||||
|
||||
syncManager.AddSlave(instance.GameplayClock);
|
||||
syncManager.AddPlayerClock(instance.GameplayClock);
|
||||
leaderboard.AddClock(instance.UserId, instance.GameplayClock);
|
||||
}
|
||||
|
||||
|
@ -35,10 +35,10 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
|
||||
public readonly int UserId;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="ISlaveClock"/> used to control the gameplay running state of a loaded <see cref="Player"/>.
|
||||
/// The <see cref="ISpectatorPlayerClock"/> used to control the gameplay running state of a loaded <see cref="Player"/>.
|
||||
/// </summary>
|
||||
[NotNull]
|
||||
public readonly ISlaveClock GameplayClock;
|
||||
public readonly ISpectatorPlayerClock GameplayClock;
|
||||
|
||||
/// <summary>
|
||||
/// The currently-loaded score.
|
||||
@ -54,7 +54,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
|
||||
private readonly AudioContainer audioContainer;
|
||||
private OsuScreenStack stack;
|
||||
|
||||
public PlayerArea(int userId, [NotNull] ISlaveClock gameplayClock)
|
||||
public PlayerArea(int userId, [NotNull] ISpectatorPlayerClock gameplayClock)
|
||||
{
|
||||
UserId = userId;
|
||||
GameplayClock = gameplayClock;
|
||||
|
@ -8,9 +8,9 @@ using osu.Framework.Timing;
|
||||
namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate.Sync
|
||||
{
|
||||
/// <summary>
|
||||
/// A <see cref="ISlaveClock"/> which catches up using rate adjustment.
|
||||
/// A <see cref="ISpectatorPlayerClock"/> which catches up using rate adjustment.
|
||||
/// </summary>
|
||||
public class CatchUpSlaveClock : ISlaveClock
|
||||
public class CatchUpSpectatorPlayerClock : ISpectatorPlayerClock
|
||||
{
|
||||
/// <summary>
|
||||
/// The catch up rate.
|
||||
@ -19,7 +19,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate.Sync
|
||||
|
||||
private readonly IFrameBasedClock masterClock;
|
||||
|
||||
public CatchUpSlaveClock(IFrameBasedClock masterClock)
|
||||
public CatchUpSpectatorPlayerClock(IFrameBasedClock masterClock)
|
||||
{
|
||||
this.masterClock = masterClock;
|
||||
}
|
@ -9,46 +9,46 @@ using osu.Framework.Timing;
|
||||
namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate.Sync
|
||||
{
|
||||
/// <summary>
|
||||
/// A <see cref="ISyncManager"/> which synchronises de-synced slave clocks through catchup.
|
||||
/// A <see cref="ISyncManager"/> which synchronises de-synced player clocks through catchup.
|
||||
/// </summary>
|
||||
public class CatchUpSyncManager : Component, ISyncManager
|
||||
{
|
||||
/// <summary>
|
||||
/// The offset from the master clock to which slaves should be synchronised to.
|
||||
/// The offset from the master clock to which player clocks should be synchronised to.
|
||||
/// </summary>
|
||||
public const double SYNC_TARGET = 16;
|
||||
|
||||
/// <summary>
|
||||
/// The offset from the master clock at which slaves begin resynchronising.
|
||||
/// The offset from the master clock at which player clocks begin resynchronising.
|
||||
/// </summary>
|
||||
public const double MAX_SYNC_OFFSET = 50;
|
||||
|
||||
/// <summary>
|
||||
/// The maximum delay to start gameplay, if any (but not all) slaves are ready.
|
||||
/// The maximum delay to start gameplay, if any (but not all) player clocks are ready.
|
||||
/// </summary>
|
||||
public const double MAXIMUM_START_DELAY = 15000;
|
||||
|
||||
/// <summary>
|
||||
/// The master clock which is used to control the timing of all slave clocks.
|
||||
/// The master clock which is used to control the timing of all player clocks clocks.
|
||||
/// </summary>
|
||||
public IAdjustableClock Master { get; }
|
||||
public IAdjustableClock MasterClock { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The slave clocks.
|
||||
/// The player clocks.
|
||||
/// </summary>
|
||||
private readonly List<ISlaveClock> slaves = new List<ISlaveClock>();
|
||||
private readonly List<ISpectatorPlayerClock> playerClocks = new List<ISpectatorPlayerClock>();
|
||||
|
||||
private bool hasStarted;
|
||||
private double? firstStartAttemptTime;
|
||||
|
||||
public CatchUpSyncManager(IAdjustableClock master)
|
||||
{
|
||||
Master = master;
|
||||
MasterClock = master;
|
||||
}
|
||||
|
||||
public void AddSlave(ISlaveClock clock) => slaves.Add(clock);
|
||||
public void AddPlayerClock(ISpectatorPlayerClock clock) => playerClocks.Add(clock);
|
||||
|
||||
public void RemoveSlave(ISlaveClock clock) => slaves.Remove(clock);
|
||||
public void RemovePlayerClock(ISpectatorPlayerClock clock) => playerClocks.Remove(clock);
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
@ -56,9 +56,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate.Sync
|
||||
|
||||
if (!attemptStart())
|
||||
{
|
||||
// Ensure all slaves are stopped until the start succeeds.
|
||||
foreach (var slave in slaves)
|
||||
slave.Stop();
|
||||
// Ensure all player clocks are stopped until the start succeeds.
|
||||
foreach (var clock in playerClocks)
|
||||
clock.Stop();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -67,7 +67,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate.Sync
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to start playback. Awaits for all slaves to have available frames for up to <see cref="MAXIMUM_START_DELAY"/> milliseconds.
|
||||
/// Attempts to start playback. Waits for all player clocks to have available frames for up to <see cref="MAXIMUM_START_DELAY"/> milliseconds.
|
||||
/// </summary>
|
||||
/// <returns>Whether playback was started and syncing should occur.</returns>
|
||||
private bool attemptStart()
|
||||
@ -75,14 +75,14 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate.Sync
|
||||
if (hasStarted)
|
||||
return true;
|
||||
|
||||
if (slaves.Count == 0)
|
||||
if (playerClocks.Count == 0)
|
||||
return false;
|
||||
|
||||
firstStartAttemptTime ??= Time.Current;
|
||||
|
||||
int readyCount = slaves.Count(s => !s.WaitingOnFrames.Value);
|
||||
int readyCount = playerClocks.Count(s => !s.WaitingOnFrames.Value);
|
||||
|
||||
if (readyCount == slaves.Count)
|
||||
if (readyCount == playerClocks.Count)
|
||||
return hasStarted = true;
|
||||
|
||||
if (readyCount > 0 && (Time.Current - firstStartAttemptTime) > MAXIMUM_START_DELAY)
|
||||
@ -92,38 +92,38 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate.Sync
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the catchup states of all slave clocks.
|
||||
/// Updates the catchup states of all player clocks clocks.
|
||||
/// </summary>
|
||||
private void updateCatchup()
|
||||
{
|
||||
for (int i = 0; i < slaves.Count; i++)
|
||||
for (int i = 0; i < playerClocks.Count; i++)
|
||||
{
|
||||
var slave = slaves[i];
|
||||
double timeDelta = Master.CurrentTime - slave.CurrentTime;
|
||||
var clock = playerClocks[i];
|
||||
double timeDelta = MasterClock.CurrentTime - clock.CurrentTime;
|
||||
|
||||
// Check that the slave isn't too far ahead.
|
||||
// This is a quiet case in which the catchup is done by the master clock, so IsCatchingUp is not set on the slave.
|
||||
// Check that the player clock isn't too far ahead.
|
||||
// This is a quiet case in which the catchup is done by the master clock, so IsCatchingUp is not set on the player clock.
|
||||
if (timeDelta < -SYNC_TARGET)
|
||||
{
|
||||
slave.Stop();
|
||||
clock.Stop();
|
||||
continue;
|
||||
}
|
||||
|
||||
// Make sure the slave is running if it can.
|
||||
if (!slave.WaitingOnFrames.Value)
|
||||
slave.Start();
|
||||
// Make sure the player clock is running if it can.
|
||||
if (!clock.WaitingOnFrames.Value)
|
||||
clock.Start();
|
||||
|
||||
if (slave.IsCatchingUp)
|
||||
if (clock.IsCatchingUp)
|
||||
{
|
||||
// Stop the slave from catching up if it's within the sync target.
|
||||
// Stop the player clock from catching up if it's within the sync target.
|
||||
if (timeDelta <= SYNC_TARGET)
|
||||
slave.IsCatchingUp = false;
|
||||
clock.IsCatchingUp = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Make the slave start catching up if it's exceeded the maximum allowable sync offset.
|
||||
// Make the player clock start catching up if it's exceeded the maximum allowable sync offset.
|
||||
if (timeDelta > MAX_SYNC_OFFSET)
|
||||
slave.IsCatchingUp = true;
|
||||
clock.IsCatchingUp = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -133,14 +133,14 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate.Sync
|
||||
/// </summary>
|
||||
private void updateMasterClock()
|
||||
{
|
||||
bool anyInSync = slaves.Any(s => !s.IsCatchingUp);
|
||||
bool anyInSync = playerClocks.Any(s => !s.IsCatchingUp);
|
||||
|
||||
if (Master.IsRunning != anyInSync)
|
||||
if (MasterClock.IsRunning != anyInSync)
|
||||
{
|
||||
if (anyInSync)
|
||||
Master.Start();
|
||||
MasterClock.Start();
|
||||
else
|
||||
Master.Stop();
|
||||
MasterClock.Stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate.Sync
|
||||
/// <summary>
|
||||
/// A clock which is used by <see cref="MultiSpectatorPlayer"/>s and managed by an <see cref="ISyncManager"/>.
|
||||
/// </summary>
|
||||
public interface ISlaveClock : IFrameBasedClock, IAdjustableClock
|
||||
public interface ISpectatorPlayerClock : IFrameBasedClock, IAdjustableClock
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether this clock is waiting on frames to continue playback.
|
@ -6,25 +6,25 @@ using osu.Framework.Timing;
|
||||
namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate.Sync
|
||||
{
|
||||
/// <summary>
|
||||
/// Manages the synchronisation between one or more <see cref="ISlaveClock"/>s in relation to a master clock.
|
||||
/// Manages the synchronisation between one or more <see cref="ISpectatorPlayerClock"/>s in relation to a master clock.
|
||||
/// </summary>
|
||||
public interface ISyncManager
|
||||
{
|
||||
/// <summary>
|
||||
/// The master clock which slaves should synchronise to.
|
||||
/// The master clock which player clocks should synchronise to.
|
||||
/// </summary>
|
||||
IAdjustableClock Master { get; }
|
||||
IAdjustableClock MasterClock { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Adds an <see cref="ISlaveClock"/> to manage.
|
||||
/// Adds an <see cref="ISpectatorPlayerClock"/> to manage.
|
||||
/// </summary>
|
||||
/// <param name="clock">The <see cref="ISlaveClock"/> to add.</param>
|
||||
void AddSlave(ISlaveClock clock);
|
||||
/// <param name="clock">The <see cref="ISpectatorPlayerClock"/> to add.</param>
|
||||
void AddPlayerClock(ISpectatorPlayerClock clock);
|
||||
|
||||
/// <summary>
|
||||
/// Removes an <see cref="ISlaveClock"/>, stopping it from being managed by this <see cref="ISyncManager"/>.
|
||||
/// Removes an <see cref="ISpectatorPlayerClock"/>, stopping it from being managed by this <see cref="ISyncManager"/>.
|
||||
/// </summary>
|
||||
/// <param name="clock">The <see cref="ISlaveClock"/> to remove.</param>
|
||||
void RemoveSlave(ISlaveClock clock);
|
||||
/// <param name="clock">The <see cref="ISpectatorPlayerClock"/> to remove.</param>
|
||||
void RemovePlayerClock(ISpectatorPlayerClock clock);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user