NetworkTime.cs 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. // NetworkTime now uses NetworkClient's snapshot interpolated timeline.
  2. // this gives ideal results & ensures everything is on the same timeline.
  3. // previously, NetworkTransforms were on separate timelines.
  4. //
  5. // however, some of the old NetworkTime code remains for ping time (rtt).
  6. // some users may still be using that.
  7. using System.Runtime.CompilerServices;
  8. using UnityEngine;
  9. #if !UNITY_2020_3_OR_NEWER
  10. using Stopwatch = System.Diagnostics.Stopwatch;
  11. #endif
  12. namespace Mirror
  13. {
  14. /// <summary>Synchronizes server time to clients.</summary>
  15. public static class NetworkTime
  16. {
  17. /// <summary>Ping message frequency, used to calculate network time and RTT</summary>
  18. public static float PingFrequency = 2;
  19. /// <summary>Average out the last few results from Ping</summary>
  20. public static int PingWindowSize = 10;
  21. static double lastPingTime;
  22. static ExponentialMovingAverage _rtt = new ExponentialMovingAverage(10);
  23. /// <summary>Returns double precision clock time _in this system_, unaffected by the network.</summary>
  24. #if UNITY_2020_3_OR_NEWER
  25. public static double localTime
  26. {
  27. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  28. get => Time.timeAsDouble;
  29. }
  30. #else
  31. // need stopwatch for older Unity versions, but it's quite slow.
  32. // CAREFUL: unlike Time.time, this is not a FRAME time.
  33. // it changes during the frame too.
  34. static readonly Stopwatch stopwatch = new Stopwatch();
  35. static NetworkTime() => stopwatch.Start();
  36. public static double localTime => stopwatch.Elapsed.TotalSeconds;
  37. #endif
  38. /// <summary>The time in seconds since the server started.</summary>
  39. // via global NetworkClient snapshot interpolated timeline (if client).
  40. // on server, this is simply Time.timeAsDouble.
  41. //
  42. // I measured the accuracy of float and I got this:
  43. // for the same day, accuracy is better than 1 ms
  44. // after 1 day, accuracy goes down to 7 ms
  45. // after 10 days, accuracy is 61 ms
  46. // after 30 days , accuracy is 238 ms
  47. // after 60 days, accuracy is 454 ms
  48. // in other words, if the server is running for 2 months,
  49. // and you cast down to float, then the time will jump in 0.4s intervals.
  50. public static double time
  51. {
  52. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  53. get => NetworkServer.active
  54. ? localTime
  55. : NetworkClient.localTimeline;
  56. }
  57. /// <summary>Clock difference in seconds between the client and the server. Always 0 on server.</summary>
  58. // original implementation used 'client - server' time. keep it this way.
  59. // TODO obsolete later. people shouldn't worry about this.
  60. public static double offset => localTime - time;
  61. /// <summary>Round trip time (in seconds) that it takes a message to go client->server->client.</summary>
  62. public static double rtt => _rtt.Value;
  63. // RuntimeInitializeOnLoadMethod -> fast playmode without domain reload
  64. [RuntimeInitializeOnLoadMethod]
  65. public static void ResetStatics()
  66. {
  67. PingFrequency = 2;
  68. PingWindowSize = 10;
  69. lastPingTime = 0;
  70. _rtt = new ExponentialMovingAverage(PingWindowSize);
  71. #if !UNITY_2020_3_OR_NEWER
  72. stopwatch.Restart();
  73. #endif
  74. }
  75. internal static void UpdateClient()
  76. {
  77. // localTime (double) instead of Time.time for accuracy over days
  78. if (localTime - lastPingTime >= PingFrequency)
  79. {
  80. NetworkPingMessage pingMessage = new NetworkPingMessage(localTime);
  81. NetworkClient.Send(pingMessage, Channels.Unreliable);
  82. lastPingTime = localTime;
  83. }
  84. }
  85. // executed at the server when we receive a ping message
  86. // reply with a pong containing the time from the client
  87. // and time from the server
  88. internal static void OnServerPing(NetworkConnectionToClient conn, NetworkPingMessage message)
  89. {
  90. // Debug.Log($"OnPingServerMessage conn:{conn}");
  91. NetworkPongMessage pongMessage = new NetworkPongMessage
  92. {
  93. clientTime = message.clientTime,
  94. serverTime = localTime
  95. };
  96. conn.Send(pongMessage, Channels.Unreliable);
  97. }
  98. // Executed at the client when we receive a Pong message
  99. // find out how long it took since we sent the Ping
  100. // and update time offset
  101. internal static void OnClientPong(NetworkPongMessage message)
  102. {
  103. // how long did this message take to come back
  104. double newRtt = localTime - message.clientTime;
  105. _rtt.Add(newRtt);
  106. }
  107. }
  108. }