Search Unity

Synchronising Network.time

Discussion in 'Multiplayer' started by bobthefish, Jul 22, 2010.

  1. bobthefish

    bobthefish

    Joined:
    Jul 7, 2010
    Posts:
    18
    I was under the impression from reading the documentation that Network.time was kept the same for all players so that you can easily synchronise, interpolate, etc.

    However this is obviously* not how it works, I'm getting vastly different Network.time values (e.g. 40174.452 on one machine and 201552.805 on another).

    Is there a way to synchronise them so they stay the same at all times on all machines connected?

    --------

    *You can easily reproduce this, just follow these steps on two different computers.
    1. Download the Networking Example from the Unity site and open the project in Unity

    2. Open Chat.js and add the following three lines:
    Code (csharp):
    1. function Update()
    2. {
    3.     Debug.Log(Network.time);
    4. }
    Save it and run the game.

    3. Host a game on one computer and join it on another computer (not in another window on the same machine)

    4. Observe the Debug log on each machine.
     
  2. bobthefish

    bobthefish

    Joined:
    Jul 7, 2010
    Posts:
    18
    ---------------------------
    (EDIT) UPDATE: I discovered a better solution than this. I'll leave this post here in case you want to use it for some reason but see my update post a few posts down for the better solution.
    ---------------------------

    I'm still interested to hear what the official solution to the problem is, but here's mine: I wrote a script to make Network.time behave the way I want, using the synchronisation method described in this article.

    Unfortunately you can't write to Network.time, so I've made another static variable (NetworkTimeSync.time) which I use instead.
    The script is below, if you want to use it just save it as NetworkTimeSync.js and attach it to an object that has a NetworkView.

    I've tested it over LAN and over internet to someone in the same city, it seems to work fine - I haven't tested it across a high latency connection though.

    Code (csharp):
    1. //Synchronised network time (NetworkTimeSync.time)
    2. //Based on method described here: [url]http://www.mine-control.com/zack/timesync/timesync.html[/url]
    3.  
    4. var timeSendDelay : float = 2; //how often to send sync requests
    5. var timeSinceLast : float = timeSendDelay;
    6. var halfLatencies = Array();
    7. static var time : double = 0;
    8.  
    9. function Start()
    10. {
    11.     //initialize array
    12.     halfLatencies = new Array();
    13. }
    14.  
    15. function OnConnectedToServer()
    16. {
    17.     //clear latencies list
    18.     halfLatencies = new Array();
    19.     timeSinceLast = timeSendDelay;
    20. }
    21.  
    22. function Update()
    23. {
    24.     if (Network.isClient)
    25.     {
    26.         time += Time.deltaTime;
    27.         timeSinceLast += Time.deltaTime;
    28.         if (timeSinceLast > timeSendDelay)
    29.         {
    30.             networkView.RPC("SyncNetClock", RPCMode.Server, time, 0.0);
    31.             timeSinceLast = 0;
    32.         }
    33.     }
    34.     else time = Network.time; //this is the only place is your program where Network.time should appear - use NetworkTimeSync.time instead
    35. }
    36.  
    37. var dataLength : int = 5; //whatever amount of data you want to find the median from
    38.  
    39. function recalculateAverage()
    40. {
    41.     var avgHalfLat : float = 0;
    42.     var hLength = halfLatencies.length;
    43.    
    44.     if (hLength > dataLength)
    45.     {
    46.         //find the median and discard anomalous samples
    47.         halfLatencies.Sort();
    48.         var midpoint : int = hLength * 0.5;
    49.         var median : float = halfLatencies[midpoint];
    50.         var stddev : float = 0;
    51.         for (var hL in halfLatencies)
    52.         {
    53.             stddev += (hL-median)*(hL-median);
    54.         }
    55.         stddev /= hLength;
    56.         stddev = Mathf.Sqrt(stddev);
    57.         //remove all entries above 1 stddev
    58.         var stdmin = median-stddev;
    59.         var stdmax = median+stddev;
    60.         for (var i : int = hLength-1; i>=0; i--)
    61.         {
    62.             var testval : float = halfLatencies[i];
    63.             if ((testval < stdmin)||(testval > stdmax))
    64.             {
    65.                 halfLatencies.RemoveAt(i);
    66.             }
    67.         }
    68.     }
    69.     if (hLength > 0)
    70.     {
    71.         //calculate arithmetic mean
    72.         for (var hL in halfLatencies)
    73.         {
    74.             avgHalfLat += hL;
    75.         }
    76.         avgHalfLat /= hLength;
    77.     }
    78.     while (halfLatencies.length > dataLength)
    79.     {
    80.         //make sure array doesn't grow out of control
    81.         halfLatencies.Pop();
    82.     }
    83.     //Debug.Log("Average latency: "+Mathf.RoundToInt(avgHalfLat*1000)+" ms");
    84.     return avgHalfLat;
    85. }
    86.  
    87. @RPC
    88. function SyncNetClock(ctime : float, stime : float, info : NetworkMessageInfo)
    89. {
    90.     if (Network.isServer)
    91.     {
    92.         //reply
    93.         networkView.RPC("SyncNetClock", info.sender, ctime, time);
    94.     }
    95.     else //if (Network.isClient)
    96.     {
    97.         var halfLatency : float = (time - ctime)/2.0;
    98.         //add to latencies list
    99.         halfLatencies.Add(halfLatency);
    100.         //compute likely average half-latency
    101.         halfLatency = recalculateAverage();
    102.         var timeDelta : float = stime - time;
    103.         time += timeDelta + halfLatency;
    104.     }
    105. }
     
  3. Dreamora

    Dreamora

    Joined:
    Apr 5, 2008
    Posts:
    26,601
    Network time is actually meant to be in sync on all.
    if it isn't in sync on one system then something must be interfering badly.

    never had it here and my win2k8 server has an uptime of weeks
     
  4. bobthefish

    bobthefish

    Joined:
    Jul 7, 2010
    Posts:
    18
    I have never had Network.time automatically equal between the server and client, ever.

    Any ideas about what might be interfering?


    Did you try the steps I posted to reproduce the problem using the Networking example project?
     
  5. jashan

    jashan

    Joined:
    Mar 9, 2007
    Posts:
    3,307
    Just curious: Are you using Unity 3 beta? There's a bug with Network.time in that version which might be causing this.
     
  6. Veli

    Veli

    Joined:
    Mar 10, 2009
    Posts:
    301
    Actually I asked this from AngryAnt awhile back and he said it was working as intended according to UT's networking guy.

    Nevertheless, that script is very nice. Mind if I use it? :p
     
  7. bobthefish

    bobthefish

    Joined:
    Jul 7, 2010
    Posts:
    18
    I'm using 2.6.

    Feel free!
    If you think of a way to improve it, post here :)
     
  8. bobthefish

    bobthefish

    Joined:
    Jul 7, 2010
    Posts:
    18
    Update on this topic:

    So after a bit of fiddling I discovered that the Network.time values are in sync, they're just not equal. I'm unsure why this is, but it allowed me to solve the problem in a much neater way than I did above. I've since removed all that code I wrote to custom-synchronise.

    The solution to the problem is to send relative times rather than absolute times. On the sender side, you use your Network.time to convert the time into a "time from now". On the receiving end, use the timestamp in the NetworkMessageInfo to convert it back into absolute time again.

    This also explains why my timestamp values weren't matching on two different computers - they are given in terms of local Network.time, rather than a shared synchronised time.
     
  9. cerebrate

    cerebrate

    Joined:
    Jan 8, 2010
    Posts:
    261
    should probably provide an example. I think this works, I haven't actually tested it though.

    Code (csharp):
    1. //attach to a gameSetup object with a networkview
    2. static var time : double;
    3. //this time is the same on EVERYONE.  Network.time is relative to the player. Network.time - info.timestamp WILL work correctly, because the timestamps are adjusted
    4.  
    5. private var netView : NetworkView;
    6.  
    7. //fixes for network.time not being same on all teh clients.  Syncs itself to server's network.time
    8. function Awake(){
    9.     netView = networkView;
    10.     if (!Network.isServer){
    11.         netView.RPC("SyncDeltaTime", RPCMode.Server);
    12.     }
    13.     else{
    14.         time = Network.time;
    15.     }
    16. }
    17.  
    18. //keep time updated
    19. function Update(){
    20.     if (Network.isServer) time = Network.time; //truthfully the server can always reference network.time while clients can reference netx.time, but this makes code a bit easier
    21.     else time = Network.time + deltaTime; //cause network.time is synched, just starts at different points, so just add the difference
    22. }
    23.  
    24. //server function
    25. @RPC
    26. function SyncDeltaTime(info : NetworkMessageInfo){
    27.     var foo : float = Network.time; //can't rpc doubles
    28.     netView.RPC("SetDeltaSync", info.sender, foo);
    29. }
    30.  
    31. var deltaTime : float;
    32. //get the difference in time from local network.time to the server's time.
    33. function SetDeltaSync(newTime : float, info : NetworkMessageInfo){
    34.     deltaTime = (newTime + (Network.time - info.timestamp)) - Network.time;  //make sure we compensate for lag.
    35.     Debug.Log("Delta from network.time to server's network.time is " + deltaTime);
    36. }
    edit: facepalm. I was calculating deltaTime wrong. It's correct now, so I believe the script works for real now.
     
  10. Dreamora

    Dreamora

    Joined:
    Apr 5, 2008
    Posts:
    26,601
    in which way is it not equal.

    keep in mind, equal means that the client has the same "simulated network time" as the server uses for timestamps etc so you can do any kind of interpolation etc.

    it will NEVER be equal between client and server if you mean equal = the same value at the same point in time. But that wouldn't make any sense at all as the timestamps are generated based on network.time not time.time. Also network.time automatically includes the impacts of latency etc. Time.time is fine for a lot but not for anything related to networking.
     
  11. bobthefish

    bobthefish

    Joined:
    Jul 7, 2010
    Posts:
    18
    I'm very confused. I thought that was the point of synchronisation? So the server could say to all the clients "this is going to happen at time X" and then time X occurs simultaneously for players?
     
  12. tbryant

    tbryant

    Joined:
    Aug 12, 2009
    Posts:
    17
    Thanks for your example cerebrate - is this the proper way to synchronize time over the network? I was naively thinking that I could use Network.time on all clients and the server, but it is different on every client. I modified your code slightly to provide time starting from 0 at the Awake of the server:

    Code (csharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4.  
    5. [RequireComponent(typeof(NetworkView))]
    6. public class NetworkStartTime : MonoBehaviour
    7. {
    8.     public static NetworkStartTime instance;
    9.     private float deltaTime;
    10.     public float time;
    11.  
    12.     void Awake ()
    13.     {
    14.         instance = this;
    15.         if(Network.isServer){
    16.             deltaTime = -(float)Network.time;
    17.         }
    18.         else{
    19.             networkView.RPC("GetServerTime",RPCMode.Server);
    20.         }
    21.     }
    22.    
    23.     void Update()
    24.     {
    25.         time = (float)Network.time + deltaTime;    
    26.     }
    27.    
    28.    
    29.     //server function
    30.     [RPC]
    31.     void GetServerTime(NetworkMessageInfo info){
    32.         networkView.RPC("SetDeltaTime", info.sender, time);
    33.     }
    34.     [RPC]
    35.     void SetDeltaTime (float serverTime, NetworkMessageInfo info)
    36.     {
    37.        deltaTime = serverTime - (float)info.timestamp;
    38.        Debug.Log("Delta " + deltaTime + "  serverTime =  " + serverTime.ToString());
    39.     }
    40. }
    41.  
    then NetworkStartTime.instance.time can be used anywhere for synchronized network time starting at 0 from the servers Awake().

    Will I correctly calculate deltaTimes when there is a large lag? It seems to me that you had (Network.time - Network.time) in your example. The timestamp matches the time the message was sent, in the local machines network time, right?

    By the way, I am using Unity 3 Beta 7, I'm under the impression that there was a bug in an earlier beta, and it was fixed. Just to clarify, is the expected behavior for Network.time to be unique on each machine?
     
  13. cerebrate

    cerebrate

    Joined:
    Jan 8, 2010
    Posts:
    261
    Yeah, the network.time seems to be actually the system's uptime of the computer it's running on.

    Anyway, Network.time - NetworkMessageInfo.timestamp is the time it took for the packet to transit, so yes, networkmessageinfo timestamps are indeed made to match the local network.time, not whoever sent it.
     
  14. Dreamora

    Dreamora

    Joined:
    Apr 5, 2008
    Posts:
    26,601
    They are in relation to the Network Time you have at hand yes.

    Anything else would be useless if they were in relation to something you don't have (the network time of anyone else in the game).

    Basing on the timestamp + network time the stuff is working fine and at least has for me worked pretty much fine since 2.1
     
  15. RR7

    RR7

    Joined:
    Jan 9, 2017
    Posts:
    254
    Is there any way this can work on UNET?

    My best efforts to make it work fail miserably.

    The client never received the updated time.
     
  16. TwoTen

    TwoTen

    Joined:
    May 25, 2016
    Posts:
    1,168
    This thread is 7 years old. You should open a new one. But to answer your question. You can sync the difference with the LLAPI methods