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

View File

@ -39,6 +39,10 @@ namespace QSB.Animation
{ {
return; return;
} }
if (_to.runtimeAnimatorController != _from.runtimeAnimatorController)
{
_to.runtimeAnimatorController = _from.runtimeAnimatorController;
}
SyncParams(); SyncParams();
SmoothFloats(); SmoothFloats();
} }
@ -52,9 +56,6 @@ namespace QSB.Animation
case AnimatorControllerParameterType.Float: case AnimatorControllerParameterType.Float:
_floatParams[fromParam.name].Target = _from.GetFloat(fromParam.name); _floatParams[fromParam.name].Target = _from.GetFloat(fromParam.name);
break; break;
case AnimatorControllerParameterType.Int:
_to.SetInteger(fromParam.name, _from.GetInteger(fromParam.name));
break;
case AnimatorControllerParameterType.Bool: case AnimatorControllerParameterType.Bool:
_to.SetBool(fromParam.name, _from.GetBool(fromParam.name)); _to.SetBool(fromParam.name, _from.GetBool(fromParam.name));
break; 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.Common;
using OWML.ModHelper; using OWML.ModHelper;
using QSB.TimeSync;
using UnityEngine; using UnityEngine;
using UnityEngine.Networking; using UnityEngine.Networking;
@ -22,7 +21,6 @@ namespace QSB
gameObject.AddComponent<DebugLog>(); gameObject.AddComponent<DebugLog>();
gameObject.AddComponent<QSBNetworkManager>(); gameObject.AddComponent<QSBNetworkManager>();
gameObject.AddComponent<NetworkManagerHUD>(); gameObject.AddComponent<NetworkManagerHUD>();
gameObject.AddComponent<PreserveTimeScale>();
gameObject.AddComponent<RespawnOnDeath>(); gameObject.AddComponent<RespawnOnDeath>();
} }
} }

View File

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

View File

@ -1,15 +1,25 @@
using UnityEngine; using OWML.ModHelper.Events;
using UnityEngine;
namespace QSB.TimeSync 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; public override MessageType MessageType => MessageType.WakeUp;
private bool _wakeUp = true; public float ServerTime { get; set; }
public override void Deserialize(NetworkReader reader) public override void Deserialize(NetworkReader reader)
{ {
_wakeUp = reader.ReadBoolean(); ServerTime = reader.ReadSingle();
} }
public override void Serialize(NetworkWriter writer) 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 QSB.Messaging;
using UnityEngine;
using UnityEngine.Networking; using UnityEngine.Networking;
using UnityEngine.SceneManagement;
namespace QSB.TimeSync namespace QSB.TimeSync
{ {
public class WakeUpSync : NetworkBehaviour 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 MessageHandler<WakeUpMessage> _wakeUpHandler;
private Campfire _campfire;
private float _sendTimer;
private float _serverTime;
private void Start() private void Start()
{ {
@ -14,20 +26,46 @@ namespace QSB.TimeSync
{ {
return; return;
} }
DebugLog.Screen("Start WakeUpSync"); DebugLog.Screen("Start WakeUpSync");
GlobalMessenger.AddListener("WakeUp", OnWakeUp); GlobalMessenger.AddListener("WakeUp", OnWakeUp);
SceneManager.sceneLoaded += OnSceneLoaded;
_wakeUpHandler = new MessageHandler<WakeUpMessage>(); _wakeUpHandler = new MessageHandler<WakeUpMessage>();
_wakeUpHandler.OnClientReceiveMessage += OnClientReceiveMessage; _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() private void OnWakeUp()
{ {
if (!isServer) _state = State.Awake;
if (isServer)
{ {
return; SendServerTime();
} }
DebugLog.Screen("Sending wakeup to all my friends"); else
_wakeUpHandler.SendToAll(new WakeUpMessage()); {
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) private void OnClientReceiveMessage(WakeUpMessage message)
@ -36,7 +74,45 @@ namespace QSB.TimeSync
{ {
return; 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. // 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. // 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"); 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 public abstract class TransformSync : NetworkBehaviour
{ {
private const float SmoothTime = 0.1f; private const float SmoothTime = 0.1f;
private static bool _isAwake;
private Transform _syncedTransform; private Transform _syncedTransform;
private bool _isSectorSetUp; private bool _isSectorSetUp;
@ -15,7 +16,14 @@ namespace QSB.TransformSync
protected virtual void Awake() protected virtual void Awake()
{ {
DontDestroyOnLoad(this); DontDestroyOnLoad(this);
GlobalMessenger.AddListener("WakeUp", OnWakeUp); if (_isAwake)
{
OnWakeUp();
}
else
{
GlobalMessenger.AddListener("WakeUp", OnWakeUp);
}
} }
protected abstract Transform InitLocalTransform(); protected abstract Transform InitLocalTransform();
@ -23,6 +31,7 @@ namespace QSB.TransformSync
private void OnWakeUp() private void OnWakeUp()
{ {
_isAwake = true;
DebugLog.Screen("Start TransformSync", netId.Value); DebugLog.Screen("Start TransformSync", netId.Value);
Invoke(nameof(SetFirstSector), 1); Invoke(nameof(SetFirstSector), 1);