NetworkConnectionToClient.cs 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Runtime.CompilerServices;
  4. using UnityEngine;
  5. namespace Mirror
  6. {
  7. public class NetworkConnectionToClient : NetworkConnection
  8. {
  9. // rpcs are collected in a buffer, and then flushed out together.
  10. // this way we don't need one NetworkMessage per rpc.
  11. // => prepares for LocalWorldState as well.
  12. // ensure max size when adding!
  13. readonly NetworkWriter reliableRpcs = new NetworkWriter();
  14. readonly NetworkWriter unreliableRpcs = new NetworkWriter();
  15. public virtual string address => Transport.active.ServerGetClientAddress(connectionId);
  16. /// <summary>NetworkIdentities that this connection can see</summary>
  17. // TODO move to server's NetworkConnectionToClient?
  18. public readonly HashSet<NetworkIdentity> observing = new HashSet<NetworkIdentity>();
  19. // unbatcher
  20. public Unbatcher unbatcher = new Unbatcher();
  21. // server runs a time snapshot interpolation for each client's local time.
  22. // this is necessary for client auth movement to still be smooth on the
  23. // server for host mode.
  24. // TODO move them along server's timeline in the future.
  25. // perhaps with an offset.
  26. // for now, keep compatibility by manually constructing a timeline.
  27. ExponentialMovingAverage driftEma;
  28. ExponentialMovingAverage deliveryTimeEma; // average delivery time (standard deviation gives average jitter)
  29. public double remoteTimeline;
  30. public double remoteTimescale;
  31. double bufferTimeMultiplier = 2;
  32. double bufferTime => NetworkServer.sendInterval * bufferTimeMultiplier;
  33. // <clienttime, snaps>
  34. readonly SortedList<double, TimeSnapshot> snapshots = new SortedList<double, TimeSnapshot>();
  35. // Snapshot Buffer size limit to avoid ever growing list memory consumption attacks from clients.
  36. public int snapshotBufferSizeLimit = 64;
  37. // ping for rtt (round trip time)
  38. // useful for statistics, lag compensation, etc.
  39. double lastPingTime = 0;
  40. internal ExponentialMovingAverage _rtt = new ExponentialMovingAverage(NetworkTime.PingWindowSize);
  41. /// <summary>Round trip time (in seconds) that it takes a message to go server->client->server.</summary>
  42. public double rtt => _rtt.Value;
  43. public NetworkConnectionToClient(int networkConnectionId)
  44. : base(networkConnectionId)
  45. {
  46. // initialize EMA with 'emaDuration' seconds worth of history.
  47. // 1 second holds 'sendRate' worth of values.
  48. // multiplied by emaDuration gives n-seconds.
  49. driftEma = new ExponentialMovingAverage(NetworkServer.sendRate * NetworkClient.snapshotSettings.driftEmaDuration);
  50. deliveryTimeEma = new ExponentialMovingAverage(NetworkServer.sendRate * NetworkClient.snapshotSettings.deliveryTimeEmaDuration);
  51. // buffer limit should be at least multiplier to have enough in there
  52. snapshotBufferSizeLimit = Mathf.Max((int)NetworkClient.snapshotSettings.bufferTimeMultiplier, snapshotBufferSizeLimit);
  53. }
  54. public void OnTimeSnapshot(TimeSnapshot snapshot)
  55. {
  56. // protect against ever growing buffer size attacks
  57. if (snapshots.Count >= snapshotBufferSizeLimit) return;
  58. // (optional) dynamic adjustment
  59. if (NetworkClient.snapshotSettings.dynamicAdjustment)
  60. {
  61. // set bufferTime on the fly.
  62. // shows in inspector for easier debugging :)
  63. bufferTimeMultiplier = SnapshotInterpolation.DynamicAdjustment(
  64. NetworkServer.sendInterval,
  65. deliveryTimeEma.StandardDeviation,
  66. NetworkClient.snapshotSettings.dynamicAdjustmentTolerance
  67. );
  68. // Debug.Log($"[Server]: {name} delivery std={serverDeliveryTimeEma.StandardDeviation} bufferTimeMult := {bufferTimeMultiplier} ");
  69. }
  70. // insert into the server buffer & initialize / adjust / catchup
  71. SnapshotInterpolation.InsertAndAdjust(
  72. snapshots,
  73. NetworkClient.snapshotSettings.bufferLimit,
  74. snapshot,
  75. ref remoteTimeline,
  76. ref remoteTimescale,
  77. NetworkServer.sendInterval,
  78. bufferTime,
  79. NetworkClient.snapshotSettings.catchupSpeed,
  80. NetworkClient.snapshotSettings.slowdownSpeed,
  81. ref driftEma,
  82. NetworkClient.snapshotSettings.catchupNegativeThreshold,
  83. NetworkClient.snapshotSettings.catchupPositiveThreshold,
  84. ref deliveryTimeEma
  85. );
  86. }
  87. public void UpdateTimeInterpolation()
  88. {
  89. // timeline starts when the first snapshot arrives.
  90. if (snapshots.Count > 0)
  91. {
  92. // progress local timeline.
  93. SnapshotInterpolation.StepTime(Time.unscaledDeltaTime, ref remoteTimeline, remoteTimescale);
  94. // progress local interpolation.
  95. // TimeSnapshot doesn't interpolate anything.
  96. // this is merely to keep removing older snapshots.
  97. SnapshotInterpolation.StepInterpolation(snapshots, remoteTimeline, out _, out _, out _);
  98. // Debug.Log($"NetworkClient SnapshotInterpolation @ {localTimeline:F2} t={t:F2}");
  99. }
  100. }
  101. // Send stage three: hand off to transport
  102. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  103. protected override void SendToTransport(ArraySegment<byte> segment, int channelId = Channels.Reliable) =>
  104. Transport.active.ServerSend(connectionId, segment, channelId);
  105. protected virtual void UpdatePing()
  106. {
  107. // localTime (double) instead of Time.time for accuracy over days
  108. if (NetworkTime.localTime >= lastPingTime + NetworkTime.PingInterval)
  109. {
  110. // TODO it would be safer for the server to store the last N
  111. // messages' timestamp and only send a message number.
  112. // This way client's can't just modify the timestamp.
  113. // predictedTime parameter is 0 because the server doesn't predict.
  114. NetworkPingMessage pingMessage = new NetworkPingMessage(NetworkTime.localTime, 0);
  115. Send(pingMessage, Channels.Unreliable);
  116. lastPingTime = NetworkTime.localTime;
  117. }
  118. }
  119. internal override void Update()
  120. {
  121. UpdatePing();
  122. base.Update();
  123. }
  124. /// <summary>Disconnects this connection.</summary>
  125. public override void Disconnect()
  126. {
  127. // set not ready and handle clientscene disconnect in any case
  128. // (might be client or host mode here)
  129. isReady = false;
  130. reliableRpcs.Position = 0;
  131. unreliableRpcs.Position = 0;
  132. Transport.active.ServerDisconnect(connectionId);
  133. // IMPORTANT: NetworkConnection.Disconnect() is NOT called for
  134. // voluntary disconnects from the other end.
  135. // -> so all 'on disconnect' cleanup code needs to be in
  136. // OnTransportDisconnect, where it's called for both voluntary
  137. // and involuntary disconnects!
  138. }
  139. internal void AddToObserving(NetworkIdentity netIdentity)
  140. {
  141. observing.Add(netIdentity);
  142. // spawn identity for this conn
  143. NetworkServer.ShowForConnection(netIdentity, this);
  144. }
  145. internal void RemoveFromObserving(NetworkIdentity netIdentity, bool isDestroyed)
  146. {
  147. observing.Remove(netIdentity);
  148. if (!isDestroyed)
  149. {
  150. // hide identity for this conn
  151. NetworkServer.HideForConnection(netIdentity, this);
  152. }
  153. }
  154. internal void RemoveFromObservingsObservers()
  155. {
  156. foreach (NetworkIdentity netIdentity in observing)
  157. {
  158. netIdentity.RemoveObserver(this);
  159. }
  160. observing.Clear();
  161. }
  162. internal void AddOwnedObject(NetworkIdentity obj)
  163. {
  164. owned.Add(obj);
  165. }
  166. internal void RemoveOwnedObject(NetworkIdentity obj)
  167. {
  168. owned.Remove(obj);
  169. }
  170. internal void DestroyOwnedObjects()
  171. {
  172. // create a copy because the list might be modified when destroying
  173. HashSet<NetworkIdentity> tmp = new HashSet<NetworkIdentity>(owned);
  174. foreach (NetworkIdentity netIdentity in tmp)
  175. {
  176. if (netIdentity != null)
  177. {
  178. // unspawn scene objects, destroy instantiated objects.
  179. // fixes: https://github.com/MirrorNetworking/Mirror/issues/3538
  180. if (netIdentity.sceneId != 0)
  181. NetworkServer.UnSpawn(netIdentity.gameObject);
  182. else
  183. NetworkServer.Destroy(netIdentity.gameObject);
  184. }
  185. }
  186. // clear the hashset because we destroyed them all
  187. owned.Clear();
  188. }
  189. }
  190. }