Skip to content

Commit

Permalink
Fix speed and pause issues (#215)
Browse files Browse the repository at this point in the history
- Don't allow new SpeedPauseResponses until the requested states have been reached
This will fix issues with multiple simultaneous button presses
- Don't wait for answers of connecting clients to pause the game
- Handle play requests just like other requests to be able to wait for completion
  • Loading branch information
kaenganxt committed Dec 19, 2020
1 parent 23c1494 commit 034b0a7
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 44 deletions.
5 changes: 5 additions & 0 deletions src/Commands/Handler/Game/SpeedPauseReachedHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ protected override void Handle(SpeedPauseReachedCommand command)
{
SpeedPauseHelper.StateReached();
_currentWaitingId = -1;

// Allow the response handler to process new requests
// We need to wait until now because responses of conflicting requests
// could arrive during the waiting phase, but need to be discarded.
SpeedPauseResponseHandler.ResetWaitingId();
}
}
}
Expand Down
10 changes: 10 additions & 0 deletions src/Commands/Handler/Game/SpeedPauseRequestHandler.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using CSM.Commands.Data.Game;
using CSM.Helpers;
using CSM.Networking;
using CSM.Networking.Status;

namespace CSM.Commands.Handler.Game
{
Expand All @@ -12,6 +14,14 @@ public SpeedPauseRequestHandler()

protected override void Handle(SpeedPauseRequestCommand command)
{
// If we are a client but not yet connected, don't answer as we're not counted towards
// the "number of required consenting clients" until we are done with loading
if (MultiplayerManager.Instance.CurrentRole == MultiplayerRole.Client &&
MultiplayerManager.Instance.CurrentClient.Status != ClientStatus.Connected)
{
return;
}

SpeedPauseHelper.PlayPauseRequest(command.SimulationPaused, command.SelectedSimulationSpeed, command.RequestId);
}
}
Expand Down
11 changes: 6 additions & 5 deletions src/Commands/Handler/Game/SpeedPauseResponseHandler.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
using CSM.Commands.Data.Game;
using CSM.Helpers;
using CSM.Util;

namespace CSM.Commands.Handler.Game
{
public class SpeedPauseResponseHandler : CommandHandler<SpeedPauseResponseCommand>
{
private int _currentWaitingId;
private static int _currentWaitingId;
private int _maxNumberOfClients;
private int _numberOfClients;
private long _highestLatency;
Expand Down Expand Up @@ -60,9 +59,6 @@ protected override void Handle(SpeedPauseResponseCommand command)

// Set waiting target for the SpeedPauseReachedCommand
SpeedPauseReachedHandler.SetWaitingFor(_currentWaitingId, _maxNumberOfClients);

// Reset waiting id
_currentWaitingId = -1;
}
}

Expand All @@ -74,5 +70,10 @@ private void ResetValues()
_highestLatency = -1;
_highestTime = -1;
}

public static void ResetWaitingId()
{
_currentWaitingId = -1;
}
}
}
76 changes: 37 additions & 39 deletions src/Helpers/SpeedPauseHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using CSM.Commands.Data.Game;
using CSM.Commands.Handler.Game;
using CSM.Networking;
using CSM.Networking.Status;
using CSM.Util;
using UnityEngine;

Expand Down Expand Up @@ -41,7 +42,7 @@ public static void PlayPauseSpeedChanged(bool pause, int speed)
{
if (_state == SpeedPauseState.Paused && !pause)
{
WaitForPlay(speed);
RequestPlay(speed);
Log.Debug($"[SpeedPauseHelper] State {SpeedPauseState.Playing} requested locally.");
}
else if (_state == SpeedPauseState.Playing && pause)
Expand All @@ -64,34 +65,30 @@ public static void PlayPauseSpeedChanged(bool pause, int speed)
/// <param name="requestId"></param>
public static void PlayPauseRequest(bool pause, int speed, int requestId)
{
if (requestId == -1) // Play requested
{
if (!pause &&
(_state == SpeedPauseState.Paused ||
_state == SpeedPauseState.WaitingForPlay))
{
Play(speed);
Log.Debug($"[SpeedPauseHelper] State {SpeedPauseState.Playing} requested remotely.");
}
}
else if (pause) // Pause requested
if (pause) // Pause requested
{
SendSpeedPauseResponse(requestId);
if (_state == SpeedPauseState.Playing)
{
_state = SpeedPauseState.PauseRequested;
Log.Debug($"[SpeedPauseHelper] State {SpeedPauseState.Paused} requested remotely.");
}
SendSpeedPauseResponse(requestId);
}
else // Speed change requested
else // Speed change or play requested
{
SendSpeedPauseResponse(requestId);
if (_state == SpeedPauseState.Playing)
{
_state = SpeedPauseState.SpeedChangeRequested;
_speed = speed;
Log.Debug("[SpeedPauseHelper] Speed change requested remotely.");
}
else if (_state == SpeedPauseState.Paused)
{
_state = SpeedPauseState.PlayRequested;
_speed = speed;
Log.Debug($"[SpeedPauseHelper] State {SpeedPauseState.Playing} requested remotely.");
}
SendSpeedPauseResponse(requestId);
}
}

Expand All @@ -116,6 +113,13 @@ public static void SpeedPauseResponseReceived(long highestGameTime, long highest
_state = SpeedPauseState.WaitingForSpeedChange;
_waitTargetTime = pauseTime;
}
else if (_state == SpeedPauseState.PlayRequested)
{
_state = SpeedPauseState.WaitingForPlay;

// TODO: Maybe find a way to start the games simultaneously
_waitTargetTime = DateTime.Now;
}
}

/// <summary>
Expand Down Expand Up @@ -146,7 +150,8 @@ public static void ChangePauseStateIfNeeded()
{
SimulationManager.instance.SimulationPaused = false;
SimulationManager.instance.SelectedSimulationSpeed = _speed;
_state = SpeedPauseState.Playing;
_state = SpeedPauseState.PlayingWaiting;
SendReached();

// Clear queued drop frames because those that arrived during paused state can be ignored
SlowdownHelper.ClearDropFrames();
Expand All @@ -171,36 +176,23 @@ public static void StateReached()
Log.Debug($"[SpeedPauseHelper] State {_state} reached!");
}

/// <summary>
/// Start playing with given speed now.
/// Sets state to WaitingForPlay with a target time of now.
/// </summary>
/// <param name="speed">The speed to start with.</param>
private static void Play(int speed)
private static void RequestPlay(int speed)
{
_state = SpeedPauseState.WaitingForPlay;
_speed = speed;
_waitTargetTime = DateTime.Now;
}
_state = SpeedPauseState.PlayRequested;

/// <summary>
/// Schedule state to change to Playing after the minimal network latency time has passed.
/// Sets state to WaitingForPlay.
/// Sends a SpeedPauseRequest.
/// </summary>
/// <param name="speed">The speed to start with.</param>
private static void WaitForPlay(int speed)
{
_state = SpeedPauseState.WaitingForPlay;
_speed = speed;
_waitTargetTime = DateTime.Now.AddMilliseconds(GetMinimumLatency());
InitRand();

int requestId = _rand.Next();

Command.SendToAll(new SpeedPauseRequestCommand()
{
RequestId = -1,
RequestId = requestId,
SimulationPaused = false,
SelectedSimulationSpeed = speed
});

_speed = speed;
SendSpeedPauseResponse(requestId);
}

/// <summary>
Expand Down Expand Up @@ -263,7 +255,8 @@ private static void SendSpeedPauseResponse(int requestId)
numClients = -1;
break;
case MultiplayerRole.Server:
numClients = MultiplayerManager.Instance.PlayerList.Count;
numClients = 1 + MultiplayerManager.Instance.CurrentServer.ConnectedPlayers
.Count(p => p.Value.Status == ClientStatus.Connected);
break;
default:
numClients = 1;
Expand Down Expand Up @@ -397,6 +390,11 @@ private enum SpeedPauseState
/// </summary>
Paused,
/// <summary>
/// The play state is requested, we wait for other games to respond to the request.
/// No button clicks are allowed.
/// </summary>
PlayRequested,
/// <summary>
/// The game is waiting until waitTargetTime (realtime) is reached to change the state to Playing.
/// No button clicks are allowed.
/// </summary>
Expand Down

0 comments on commit 034b0a7

Please sign in to comment.