quantum-space-buddies/QSB/TimeSync/WakeUpSync.cs
2020-08-10 18:17:54 +02:00

232 lines
5.7 KiB
C#

using QSB.Messaging;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.SceneManagement;
namespace QSB.TimeSync
{
public class WakeUpSync : NetworkBehaviour
{
public static WakeUpSync LocalInstance { get; private set; }
private const float TimeThreshold = 0.5f;
private const float MaxFastForwardSpeed = 60f;
private const float MaxFastForwardDiff = 20f;
private const float MinFastForwardSpeed = 2f;
private enum State { NotLoaded, Loaded, FastForwarding, Pausing }
private State _state = State.NotLoaded;
private float _sendTimer;
private float _serverTime;
private float _timeScale;
private bool _isInputEnabled = true;
private int _localLoopCount;
private int _serverLoopCount;
public override void OnStartLocalPlayer()
{
LocalInstance = this;
}
private void Start()
{
if (!isLocalPlayer)
{
return;
}
var sceneName = SceneManager.GetActiveScene().name;
if (sceneName == "SolarSystem" || sceneName == "EyeOfTheUniverse")
{
Init();
}
else
{
SceneManager.sceneLoaded += OnSceneLoaded;
}
GlobalMessenger.AddListener("RestartTimeLoop", OnLoopStart);
}
private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
{
if (scene.name == "SolarSystem" || scene.name == "EyeOfTheUniverse")
{
Init();
}
else
{
Reset();
}
}
private void OnLoopStart()
{
_localLoopCount++;
}
private void Init()
{
GlobalMessenger.FireEvent("QSBPlayerStatesRequest");
_state = State.Loaded;
gameObject.AddComponent<PreserveTimeScale>();
if (isServer)
{
SendServerTime();
}
else
{
WakeUpOrSleep();
}
}
private void Reset()
{
_state = State.NotLoaded;
}
private void SendServerTime()
{
GlobalMessenger<float, int>.FireEvent("QSBServerTime", Time.timeSinceLevelLoad, _localLoopCount);
}
public void OnClientReceiveMessage(ServerTimeMessage message)
{
if (isServer)
{
return;
}
_serverTime = message.ServerTime;
_serverLoopCount = message.LoopCount;
WakeUpOrSleep();
}
private void WakeUpOrSleep()
{
if (_state == State.NotLoaded || _localLoopCount != _serverLoopCount)
{
return;
}
var myTime = Time.timeSinceLevelLoad;
var diff = myTime - _serverTime;
if (diff > TimeThreshold)
{
StartPausing();
return;
}
if (diff < -TimeThreshold)
{
StartFastForwarding();
}
}
private void StartFastForwarding()
{
if (_state == State.FastForwarding)
{
return;
}
_timeScale = MaxFastForwardSpeed;
_state = State.FastForwarding;
}
private void StartPausing()
{
if (_state == State.Pausing)
{
return;
}
_timeScale = 0f;
_state = State.Pausing;
}
private void ResetTimeScale()
{
_timeScale = 1f;
_state = State.Loaded;
if (!_isInputEnabled)
{
EnableInput();
}
}
private void DisableInput()
{
_isInputEnabled = false;
OWInput.ChangeInputMode(InputMode.None);
}
private void EnableInput()
{
_isInputEnabled = true;
OWInput.ChangeInputMode(InputMode.Character);
}
private void Update()
{
if (isServer)
{
UpdateServer();
}
else if (isLocalPlayer)
{
UpdateLocal();
}
}
private void UpdateServer()
{
if (_state != State.Loaded)
{
return;
}
_sendTimer += Time.unscaledDeltaTime;
if (_sendTimer > 1)
{
SendServerTime();
_sendTimer = 0;
}
}
private void UpdateLocal()
{
_serverTime += Time.unscaledDeltaTime;
if (_state == State.NotLoaded)
{
return;
}
if (_state == State.FastForwarding)
{
var diff = _serverTime - Time.timeSinceLevelLoad;
Time.timeScale = Mathf.Lerp(MinFastForwardSpeed, MaxFastForwardSpeed, Mathf.Abs(diff) / MaxFastForwardDiff);
}
else
{
Time.timeScale = _timeScale;
}
var isDoneFastForwarding = _state == State.FastForwarding && Time.timeSinceLevelLoad >= _serverTime;
var isDonePausing = _state == State.Pausing && Time.timeSinceLevelLoad < _serverTime;
if (isDoneFastForwarding || isDonePausing)
{
ResetTimeScale();
}
if (!_isInputEnabled && OWInput.GetInputMode() != InputMode.None)
{
DisableInput();
}
}
}
}