/// <summary> /// This method is called every tick and checks if the waitTargetTime has already been reached. /// </summary> public static void ChangePauseStateIfNeeded() { if (_state == SpeedPauseState.WaitingForPause && GetCurrentTime() > _waitTargetTime) { SimulationManager.instance.SimulationPaused = true; _state = SpeedPauseState.PausedWaiting; SendReached(); // Clear queued drop frames because we decided on an exact pause time, so the games are already in sync SlowdownHelper.ClearDropFrames(); } else if (_state == SpeedPauseState.WaitingForSpeedChange && GetCurrentTime() > _waitTargetTime) { SimulationManager.instance.SelectedSimulationSpeed = _speed; _state = SpeedPauseState.PlayingWaiting; SendReached(); // Don't clear dropped frames here because the game still continues. // Even if the current amount is not correct anymore because the dropped frames were // recorded while using a different speed, it's still an acceptable approximation. } else if (_state == SpeedPauseState.WaitingForPlay && DateTime.Now >= _waitTargetTime) { SimulationManager.instance.SimulationPaused = false; SimulationManager.instance.SelectedSimulationSpeed = _speed; _state = SpeedPauseState.Playing; // Clear queued drop frames because those that arrived during paused state can be ignored SlowdownHelper.ClearDropFrames(); } }
/// <summary> /// Called when a SpeedPauseRequest command was received. /// </summary> /// <param name="pause"></param> /// <param name="speed"></param> /// <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); _logger.Debug($"[SpeedPauseHelper] State {SpeedPauseState.Playing} requested remotely."); } } else if (pause) // Pause requested { SendSpeedPauseResponse(requestId); if (_state == SpeedPauseState.Playing) { _state = SpeedPauseState.PauseRequested; _logger.Debug($"[SpeedPauseHelper] State {SpeedPauseState.Paused} requested remotely."); } } else // Speed change requested { SendSpeedPauseResponse(requestId); if (_state == SpeedPauseState.Playing) { _state = SpeedPauseState.SpeedChangeRequested; _speed = speed; _logger.Debug("[SpeedPauseHelper] Speed change requested remotely."); } } }
/// <summary> /// Called when all responses to a pause or speed change request have been received. /// </summary> /// <param name="highestGameTime">The highest game time of all responses.</param> /// <param name="highestLatency">The highest latency of all responses.</param> public static void SpeedPauseResponseReceived(long highestGameTime, long highestLatency) { // Pause time is computed by taking the highest game time plus 4 times the maximum latency because this // is the worst case roundtrip time from client1 -> server -> client2 -> server -> client1 which means // that this amount of time may have already passed since the highest game time was determined. DateTime pauseTime = new DateTime(highestGameTime) + MillisecondsToInGameTime(highestLatency * 4); if (_state == SpeedPauseState.PauseRequested) { _state = SpeedPauseState.WaitingForPause; _waitTargetTime = pauseTime; } else if (_state == SpeedPauseState.SpeedChangeRequested) { _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> /// Called when a SpeedPauseRequest command was received. /// </summary> /// <param name="pause"></param> /// <param name="speed"></param> /// <param name="requestId"></param> public static void PlayPauseRequest(bool pause, int speed, int requestId) { if (pause) // Pause requested { if (_state == SpeedPauseState.Playing) { _state = SpeedPauseState.PauseRequested; Log.Debug($"[SpeedPauseHelper] State {SpeedPauseState.Paused} requested remotely."); } SendSpeedPauseResponse(requestId); } else // Speed change or play requested { 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); } }
/// <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()); Command.SendToAll(new SpeedPauseRequestCommand() { RequestId = -1, SimulationPaused = false, SelectedSimulationSpeed = speed }); }
/// <summary> /// Called when all SpeedPauseReached commands to a request have been received. /// This will allow the player to press the buttons again. /// </summary> public static void StateReached() { if (_state == SpeedPauseState.PlayingWaiting) { _state = SpeedPauseState.Playing; } else if (_state == SpeedPauseState.PausedWaiting) { _state = SpeedPauseState.Paused; } _logger.Debug($"[SpeedPauseHelper] State {_state} reached!"); }
/// <summary> /// Request the pause state. /// Sets state to PauseRequested. /// Sends a SpeedPauseRequest and -Response with a randomly generated request id. /// </summary> private static void RequestPause() { _state = SpeedPauseState.PauseRequested; InitRand(); int requestId = _rand.Next(); Command.SendToAll(new SpeedPauseRequestCommand() { RequestId = requestId, SimulationPaused = true }); SendSpeedPauseResponse(requestId); }
/// <summary> /// Request a speed change (while playing). /// Sets state to SpeedChangeRequested. /// Sends a SpeedPauseRequest and -Response with a randomly generated request id. /// </summary> /// <param name="speed">The target speed to request.</param> private static void RequestSpeedChange(int speed) { _state = SpeedPauseState.SpeedChangeRequested; InitRand(); int requestId = _rand.Next(); Command.SendToAll(new SpeedPauseRequestCommand() { RequestId = requestId, SimulationPaused = false, SelectedSimulationSpeed = speed }); _speed = speed; SendSpeedPauseResponse(requestId); }
/// <summary> /// Called when all SpeedPauseReached commands to a request have been received. /// This will allow the player to press the buttons again. /// </summary> public static void StateReached() { if (_state == SpeedPauseState.PlayingWaiting) { _state = SpeedPauseState.Playing; } else if (_state == SpeedPauseState.PausedWaiting) { _state = SpeedPauseState.Paused; // If a connection request is pending, we complete it here, since now all games are paused. if (ConnectionRequestHandler.WorldLoadingPlayer != null) { ConnectionRequestHandler.AllGamesBlocked(); } } Log.Debug($"[SpeedPauseHelper] State {_state} reached!"); }
/// <summary> /// Initialize current speed and pause states. /// </summary> /// <param name="paused">If the game is currently paused.</param> /// <param name="speed">Current game speed from 0 to 3.</param> public static void Initialize(bool paused, int speed) { _state = paused ? SpeedPauseState.Paused : SpeedPauseState.Playing; _speed = speed; }
/// <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) { _state = SpeedPauseState.WaitingForPlay; _speed = speed; _waitTargetTime = DateTime.Now; }