NetworkConnectionToClient.cs 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  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. public override string address =>
  10. Transport.active.ServerGetClientAddress(connectionId);
  11. /// <summary>NetworkIdentities that this connection can see</summary>
  12. // TODO move to server's NetworkConnectionToClient?
  13. public new readonly HashSet<NetworkIdentity> observing = new HashSet<NetworkIdentity>();
  14. [Obsolete(".clientOwnedObjects was renamed to .owned :)")] // 2022-10-13
  15. public new HashSet<NetworkIdentity> clientOwnedObjects => owned;
  16. // unbatcher
  17. public Unbatcher unbatcher = new Unbatcher();
  18. // server runs a time snapshot interpolation for each client's local time.
  19. // this is necessary for client auth movement to still be smooth on the
  20. // server for host mode.
  21. // TODO move them along server's timeline in the future.
  22. // perhaps with an offset.
  23. // for now, keep compatibility by manually constructing a timeline.
  24. ExponentialMovingAverage driftEma;
  25. ExponentialMovingAverage deliveryTimeEma; // average delivery time (standard deviation gives average jitter)
  26. public double remoteTimeline;
  27. public double remoteTimescale;
  28. double bufferTimeMultiplier = 2;
  29. double bufferTime => NetworkServer.sendInterval * bufferTimeMultiplier;
  30. // <clienttime, snaps>
  31. readonly SortedList<double, TimeSnapshot> snapshots = new SortedList<double, TimeSnapshot>();
  32. // Snapshot Buffer size limit to avoid ever growing list memory consumption attacks from clients.
  33. public int snapshotBufferSizeLimit = 64;
  34. public NetworkConnectionToClient(int networkConnectionId)
  35. : base(networkConnectionId)
  36. {
  37. // initialize EMA with 'emaDuration' seconds worth of history.
  38. // 1 second holds 'sendRate' worth of values.
  39. // multiplied by emaDuration gives n-seconds.
  40. driftEma = new ExponentialMovingAverage(NetworkServer.sendRate * NetworkClient.driftEmaDuration);
  41. deliveryTimeEma = new ExponentialMovingAverage(NetworkServer.sendRate * NetworkClient.deliveryTimeEmaDuration);
  42. // buffer limit should be at least multiplier to have enough in there
  43. snapshotBufferSizeLimit = Mathf.Max((int)NetworkClient.bufferTimeMultiplier, snapshotBufferSizeLimit);
  44. }
  45. public void OnTimeSnapshot(TimeSnapshot snapshot)
  46. {
  47. // protect against ever growing buffer size attacks
  48. if (snapshots.Count >= snapshotBufferSizeLimit) return;
  49. // (optional) dynamic adjustment
  50. if (NetworkClient.dynamicAdjustment)
  51. {
  52. // set bufferTime on the fly.
  53. // shows in inspector for easier debugging :)
  54. bufferTimeMultiplier = SnapshotInterpolation.DynamicAdjustment(
  55. NetworkServer.sendInterval,
  56. deliveryTimeEma.StandardDeviation,
  57. NetworkClient.dynamicAdjustmentTolerance
  58. );
  59. // Debug.Log($"[Server]: {name} delivery std={serverDeliveryTimeEma.StandardDeviation} bufferTimeMult := {bufferTimeMultiplier} ");
  60. }
  61. // insert into the server buffer & initialize / adjust / catchup
  62. SnapshotInterpolation.InsertAndAdjust(
  63. snapshots,
  64. snapshot,
  65. ref remoteTimeline,
  66. ref remoteTimescale,
  67. NetworkServer.sendInterval,
  68. bufferTime,
  69. NetworkClient.catchupSpeed,
  70. NetworkClient.slowdownSpeed,
  71. ref driftEma,
  72. NetworkClient.catchupNegativeThreshold,
  73. NetworkClient.catchupPositiveThreshold,
  74. ref deliveryTimeEma
  75. );
  76. }
  77. public void UpdateTimeInterpolation()
  78. {
  79. // timeline starts when the first snapshot arrives.
  80. if (snapshots.Count > 0)
  81. {
  82. // progress local timeline.
  83. SnapshotInterpolation.StepTime(Time.unscaledDeltaTime, ref remoteTimeline, remoteTimescale);
  84. // progress local interpolation.
  85. // TimeSnapshot doesn't interpolate anything.
  86. // this is merely to keep removing older snapshots.
  87. SnapshotInterpolation.StepInterpolation(snapshots, remoteTimeline, out _, out _, out _);
  88. // Debug.Log($"NetworkClient SnapshotInterpolation @ {localTimeline:F2} t={t:F2}");
  89. }
  90. }
  91. // Send stage three: hand off to transport
  92. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  93. protected override void SendToTransport(ArraySegment<byte> segment, int channelId = Channels.Reliable) =>
  94. Transport.active.ServerSend(connectionId, segment, channelId);
  95. /// <summary>Disconnects this connection.</summary>
  96. public override void Disconnect()
  97. {
  98. // set not ready and handle clientscene disconnect in any case
  99. // (might be client or host mode here)
  100. isReady = false;
  101. Transport.active.ServerDisconnect(connectionId);
  102. // IMPORTANT: NetworkConnection.Disconnect() is NOT called for
  103. // voluntary disconnects from the other end.
  104. // -> so all 'on disconnect' cleanup code needs to be in
  105. // OnTransportDisconnect, where it's called for both voluntary
  106. // and involuntary disconnects!
  107. }
  108. internal void AddToObserving(NetworkIdentity netIdentity)
  109. {
  110. observing.Add(netIdentity);
  111. // spawn identity for this conn
  112. NetworkServer.ShowForConnection(netIdentity, this);
  113. }
  114. internal void RemoveFromObserving(NetworkIdentity netIdentity, bool isDestroyed)
  115. {
  116. observing.Remove(netIdentity);
  117. if (!isDestroyed)
  118. {
  119. // hide identity for this conn
  120. NetworkServer.HideForConnection(netIdentity, this);
  121. }
  122. }
  123. internal void RemoveFromObservingsObservers()
  124. {
  125. foreach (NetworkIdentity netIdentity in observing)
  126. {
  127. netIdentity.RemoveObserver(this);
  128. }
  129. observing.Clear();
  130. }
  131. internal void AddOwnedObject(NetworkIdentity obj)
  132. {
  133. owned.Add(obj);
  134. }
  135. internal void RemoveOwnedObject(NetworkIdentity obj)
  136. {
  137. owned.Remove(obj);
  138. }
  139. internal void DestroyOwnedObjects()
  140. {
  141. // create a copy because the list might be modified when destroying
  142. HashSet<NetworkIdentity> tmp = new HashSet<NetworkIdentity>(owned);
  143. foreach (NetworkIdentity netIdentity in tmp)
  144. {
  145. if (netIdentity != null)
  146. {
  147. NetworkServer.Destroy(netIdentity.gameObject);
  148. }
  149. }
  150. // clear the hashset because we destroyed them all
  151. owned.Clear();
  152. }
  153. }
  154. }