Alek/sleep sync (#29)

* syncing time
* clients auto-sleep or pause to reach server's time
* disabled manual sleep/pause for clients
This commit is contained in:
AmazingAlek 2020-02-28 22:07:39 +01:00 committed by GitHub
parent d6f8112bbb
commit 188bbad5e7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 282 additions and 25 deletions

View File

@ -11,7 +11,7 @@ namespace QSB.Animation
{
private Animator _anim;
private Animator _bodyAnim;
private NetworkAnimator _netAnim;
private QSBNetAnim _netAnim;
private MessageHandler<AnimTriggerMessage> _triggerHandler;
private RuntimeAnimatorController _suitedAnimController;
@ -23,9 +23,15 @@ namespace QSB.Animation
private void Awake()
{
_anim = gameObject.AddComponent<Animator>();
_netAnim = gameObject.AddComponent<NetworkAnimator>();
_netAnim.animator = _anim;
if (_anim == null)
{
_anim = gameObject.AddComponent<Animator>();
}
if (_netAnim == null)
{
_netAnim = gameObject.AddComponent<QSBNetAnim>();
_netAnim.animator = _anim;
}
}
public void InitLocal(Transform body)
@ -50,6 +56,7 @@ namespace QSB.Animation
public void InitRemote(Transform body)
{
Awake();
_bodyAnim = body.GetComponent<Animator>();
body.gameObject.AddComponent<AnimatorMirror>().Init(_anim, _bodyAnim);
@ -106,8 +113,7 @@ namespace QSB.Animation
private void OnClientReceiveMessage(AnimTriggerMessage message)
{
var animSync = PlayerAnimSyncs[message.SenderId];
if (animSync != this)
if (PlayerAnimSyncs.TryGetValue(message.SenderId, out var animSync) && animSync != this)
{
animSync.HandleTrigger((AnimTrigger)message.TriggerId);
}

View File

@ -39,6 +39,10 @@ namespace QSB.Animation
{
return;
}
if (_to.runtimeAnimatorController != _from.runtimeAnimatorController)
{
_to.runtimeAnimatorController = _from.runtimeAnimatorController;
}
SyncParams();
SmoothFloats();
}
@ -52,9 +56,6 @@ namespace QSB.Animation
case AnimatorControllerParameterType.Float:
_floatParams[fromParam.name].Target = _from.GetFloat(fromParam.name);
break;
case AnimatorControllerParameterType.Int:
_to.SetInteger(fromParam.name, _from.GetInteger(fromParam.name));
break;
case AnimatorControllerParameterType.Bool:
_to.SetBool(fromParam.name, _from.GetBool(fromParam.name));
break;

View File

@ -0,0 +1,67 @@
using System;
using OWML.ModHelper.Events;
using UnityEngine;
using UnityEngine.Networking;
namespace QSB.Animation
{
public class QSBNetAnim : NetworkAnimator
{
public override void OnDeserialize(NetworkReader reader, bool initialState)
{
var anim = this.GetValue<Animator>("m_Animator");
if (anim == null)
{
DebugLog.Screen("anim is null");
return;
}
if (anim.parameters == null)
{
DebugLog.Screen("anim.parameters is null");
return;
}
if (anim.parameters.Length == 0)
{
DebugLog.Screen("anim.parameters.Length == 0");
return;
}
for (int index = 0; index < anim.parameters.Length; ++index)
{
if (!this.GetValue<bool>("autoSend") || this.GetParameterAutoSend(index))
{
AnimatorControllerParameter parameter = anim.parameters[index];
if (parameter.type == AnimatorControllerParameterType.Int)
{
int num = (int)reader.ReadPackedUInt32();
anim.SetInteger(parameter.nameHash, num);
this.Invoke("SetRecvTrackingParam", parameter.name + ":" + (object)num, index);
}
if (parameter.type == AnimatorControllerParameterType.Float)
{
float num;
try
{
num = reader.ReadSingle();
}
catch (Exception ex)
{
//DebugLog.Screen($"Error when reading float {parameter.name}: " + ex);
return;
}
anim.SetFloat(parameter.nameHash, num);
this.Invoke("SetRecvTrackingParam", parameter.name + ":" + (object)num, index);
}
if (parameter.type == AnimatorControllerParameterType.Bool)
{
bool flag = reader.ReadBoolean();
anim.SetBool(parameter.nameHash, flag);
this.Invoke("SetRecvTrackingParam", parameter.name + ":" + (object)flag, index);
}
}
}
}
}
}

View File

@ -1,6 +1,5 @@
using OWML.Common;
using OWML.ModHelper;
using QSB.TimeSync;
using UnityEngine;
using UnityEngine.Networking;
@ -22,7 +21,6 @@ namespace QSB
gameObject.AddComponent<DebugLog>();
gameObject.AddComponent<QSBNetworkManager>();
gameObject.AddComponent<NetworkManagerHUD>();
gameObject.AddComponent<PreserveTimeScale>();
gameObject.AddComponent<RespawnOnDeath>();
}
}

View File

@ -99,6 +99,7 @@
<Compile Include="Animation\AnimFloatParam.cs" />
<Compile Include="Animation\AnimTriggerMessage.cs" />
<Compile Include="Animation\AnimTrigger.cs" />
<Compile Include="Animation\QSBNetAnim.cs" />
<Compile Include="DebugLog.cs" />
<Compile Include="Messaging\MessageHandler.cs" />
<Compile Include="Messaging\MessageType.cs" />

View File

@ -1,15 +1,25 @@
using UnityEngine;
using OWML.ModHelper.Events;
using UnityEngine;
namespace QSB.TimeSync
{
public class PreserveTimeScale : QSBBehaviour
public class PreserveTimeScale : MonoBehaviour
{
private void Update()
private void Start()
{
if (IsPlayerAwake)
GlobalMessenger.AddListener("GamePaused", OnPause);
var campfires = GameObject.FindObjectsOfType<Campfire>();
foreach (var campfire in campfires)
{
Time.timeScale = 1;
campfire.SetValue("_canSleepHere", false);
}
}
private void OnPause()
{
Time.timeScale = 1;
}
}
}

View File

@ -7,16 +7,16 @@ namespace QSB.TimeSync
{
public override MessageType MessageType => MessageType.WakeUp;
private bool _wakeUp = true;
public float ServerTime { get; set; }
public override void Deserialize(NetworkReader reader)
{
_wakeUp = reader.ReadBoolean();
ServerTime = reader.ReadSingle();
}
public override void Serialize(NetworkWriter writer)
{
writer.Write(_wakeUp);
writer.Write(ServerTime);
}
}

View File

@ -1,12 +1,24 @@
using OWML.ModHelper.Events;
using System.Linq;
using OWML.ModHelper.Events;
using QSB.Messaging;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.SceneManagement;
namespace QSB.TimeSync
{
public class WakeUpSync : NetworkBehaviour
{
private const float TimeThreshold = 0.5f;
private enum State { NotLoaded, EyesClosed, Awake, Sleeping, Pausing }
private State _state = State.NotLoaded;
private MessageHandler<WakeUpMessage> _wakeUpHandler;
private Campfire _campfire;
private float _sendTimer;
private float _serverTime;
private void Start()
{
@ -14,20 +26,46 @@ namespace QSB.TimeSync
{
return;
}
DebugLog.Screen("Start WakeUpSync");
GlobalMessenger.AddListener("WakeUp", OnWakeUp);
SceneManager.sceneLoaded += OnSceneLoaded;
_wakeUpHandler = new MessageHandler<WakeUpMessage>();
_wakeUpHandler.OnClientReceiveMessage += OnClientReceiveMessage;
}
private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
{
if (scene.name == "SolarSystem")
{
_campfire = GameObject.FindObjectsOfType<Campfire>().Single(x => x.GetValue<Sector>("_sector").name == "Sector_Village");
_state = State.EyesClosed;
}
}
private void OnWakeUp()
{
if (!isServer)
_state = State.Awake;
if (isServer)
{
return;
SendServerTime();
}
DebugLog.Screen("Sending wakeup to all my friends");
_wakeUpHandler.SendToAll(new WakeUpMessage());
else
{
gameObject.AddComponent<PreserveTimeScale>();
WakeUpOrSleep();
}
}
private void SendServerTime()
{
DebugLog.Screen("Sending server time to all my friends: " + Time.timeSinceLevelLoad);
var message = new WakeUpMessage
{
ServerTime = Time.timeSinceLevelLoad
};
_wakeUpHandler.SendToAll(message);
}
private void OnClientReceiveMessage(WakeUpMessage message)
@ -36,7 +74,45 @@ namespace QSB.TimeSync
{
return;
}
_serverTime = message.ServerTime;
WakeUpOrSleep();
}
private void WakeUpOrSleep()
{
if (_state == State.NotLoaded)
{
return;
}
if (_state == State.EyesClosed)
{
OpenEyes();
_state = State.Awake;
}
var myTime = Time.timeSinceLevelLoad;
var diff = myTime - _serverTime;
if (diff > TimeThreshold)
{
DebugLog.Screen($"My time ({myTime}) is {diff} ahead server ({_serverTime})");
StartPausing();
return;
}
if (diff < -TimeThreshold)
{
DebugLog.Screen($"My time ({myTime}) is {-diff} behind server ({_serverTime})");
StartSleeping();
return;
}
DebugLog.Screen($"My time ({myTime}) is within threshold of server time ({_serverTime})");
}
private void OpenEyes()
{
// I copied all of this from my AutoResume mod, since that already wakes up the player instantly.
// There must be a simpler way to do this though, I just couldn't find it.
@ -59,5 +135,94 @@ namespace QSB.TimeSync
GlobalMessenger.FireEvent("TakeFirstFlashbackSnapshot");
}
private void StartSleeping()
{
if (_state == State.Sleeping)
{
return;
}
DebugLog.Screen("Starting sleeping");
_campfire.Invoke("StartSleeping");
_state = State.Sleeping;
}
private void StopSleeping()
{
if (_state != State.Sleeping)
{
return;
}
DebugLog.Screen("Stopping sleeping");
_campfire.StopSleeping();
_state = State.Awake;
}
private void StartPausing()
{
if (_state == State.Pausing)
{
return;
}
OWTime.Pause(OWTime.PauseType.Menu);
Time.timeScale = 0f;
_state = State.Pausing;
}
private void StopPausing()
{
if (_state != State.Pausing)
{
return;
}
OWTime.Unpause(OWTime.PauseType.Menu);
_state = State.Awake;
}
private void Update()
{
if (isServer)
{
UpdateServer();
}
else if (isLocalPlayer)
{
UpdateLocal();
}
}
private void UpdateServer()
{
if (_state != State.Awake)
{
return;
}
_sendTimer += Time.unscaledDeltaTime;
if (_sendTimer > 1)
{
SendServerTime();
_sendTimer = 0;
}
}
private void UpdateLocal()
{
_serverTime += Time.unscaledDeltaTime;
if (_state == State.NotLoaded || _state == State.EyesClosed)
{
return;
}
if (_state == State.Sleeping && Time.timeSinceLevelLoad >= _serverTime)
{
StopSleeping();
}
else if (_state == State.Pausing && Time.timeSinceLevelLoad < _serverTime)
{
StopPausing();
}
}
}
}

View File

@ -6,6 +6,7 @@ namespace QSB.TransformSync
public abstract class TransformSync : NetworkBehaviour
{
private const float SmoothTime = 0.1f;
private static bool _isAwake;
private Transform _syncedTransform;
private bool _isSectorSetUp;
@ -15,7 +16,14 @@ namespace QSB.TransformSync
protected virtual void Awake()
{
DontDestroyOnLoad(this);
GlobalMessenger.AddListener("WakeUp", OnWakeUp);
if (_isAwake)
{
OnWakeUp();
}
else
{
GlobalMessenger.AddListener("WakeUp", OnWakeUp);
}
}
protected abstract Transform InitLocalTransform();
@ -23,6 +31,7 @@ namespace QSB.TransformSync
private void OnWakeUp()
{
_isAwake = true;
DebugLog.Screen("Start TransformSync", netId.Value);
Invoke(nameof(SetFirstSector), 1);