I can sync it to within about .02 seconds, but I need more accuracy than that.
Here is how I do it now:
I have a client and the server. The client calls “TransmitNetworkTime” in FixedUpdate. This function is a Command that sends Network.Time and also the client’s average latency (using Network.GetRTT());.
Then, the server uses the client network time and the latency to then compare the time to the server’s own network time. ServerNetworkTime - latency - ClientNetworkTime = conversionDifference.
The problem is, this is always off by a small fraction of a second. Even if there is no latency at all(using uEcho, so I have to editors open on one computer). And it seems like this comes from the fact that, when you call a Command in FixedUpdate, this data doesn’t actually get transmitted for about .02 seconds on average, perhaps because it doesn’t get called until the end of the current frame or something?
I’m not sure exactly what it is, and I don’t know how to get around it. If I could send the network time at the exact moment that the Command actually gets sent, then I think it would fix the issue. Or maybe I’m identifying the problem incorrectly.
Yep, I got the same problem, and here’re my solution. The idea is : when a client connected to our Server, the server will send the current Network.time value to that client, the client will save the timeStamp received from Server, and update it on each frame (syncServerTime += Time.deltaTime ). So, we’ll have the syncServerTime field that be sync on both Server and connected clients.
I’ve test on my PC and some android devices, they connected to the same wifi, and the syncServerTime was correctly on all of them. Then, I use that syncServerTime instead of Network.time for other components.
Here’re my code on gist, and I’ll paste them here too :). You just need to replace the default NetworkManager component with this CustomNetworkManager component.
using UnityEngine;
using System.Collections;
using UnityEngine.Networking;
public class CustomNetworkManager : NetworkManager
{
#region Server side
public override void OnStartServer ()
{
base.OnStartServer();
// we're the Server, dont need to sync with anyone :)
_isSyncTimeWithServer = true;
syncServerTime = Network.time;
}
/// <summary>
/// On server, be called when a client connected to Server
/// </summary>
public override void OnServerConnect (NetworkConnection conn)
{
base.OnServerConnect(conn);
Debug.Log("---- Server send syncTime to client : " + conn.connectionId);
var syncTimeMessage = new SyncTimeMessage();
syncTimeMessage.timeStamp = Network.time;
NetworkServer.SendToClient(conn.connectionId, CustomMsgType.SyncTime, syncTimeMessage);
}
#endregion
#region Client side
public override void OnStartClient (NetworkClient client)
{
base.OnStartClient(client);
client.RegisterHandler(CustomMsgType.SyncTime, OnReceiveSyncTime);
}
void OnReceiveSyncTime(NetworkMessage msg)
{
var castMsg = msg.ReadMessage<SyncTimeMessage>();
_isSyncTimeWithServer = true;
syncServerTime = castMsg.timeStamp;
Debug.Log("--------Client receive : " + syncServerTime);
}
#endregion
#region Update sync time
/// <summary>
/// Use this, instead of Network.time . Sure this var will be
/// sync on both Server and all connected clients
/// </summary>
public static double syncServerTime;
/// Do we receive the syncTime from Server ?
bool _isSyncTimeWithServer = false;
void Update()
{
if (_isSyncTimeWithServer)
{
syncServerTime += Time.deltaTime;
// This Log just to show the syncTime to console for testing purpose
// remove it in your project
Debug.Log("SyncTime : " + syncServerTime);
}
}
#endregion
}
#region Custom classes
public class CustomMsgType
{
public const short SyncTime = MsgType.Highest + 1;
}
public class SyncTimeMessage : MessageBase
{
public double timeStamp;
}
#endregion