NetworkConnection.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Runtime.CompilerServices;
  4. using UnityEngine;
  5. namespace Mirror
  6. {
  7. /// <summary>Base NetworkConnection class for server-to-client and client-to-server connection.</summary>
  8. public abstract class NetworkConnection
  9. {
  10. public const int LocalConnectionId = 0;
  11. /// <summary>NetworkIdentities that this connection can see</summary>
  12. // DEPRECATED 2022-02-05
  13. [Obsolete("Cast to NetworkConnectionToClient to access .observing")]
  14. public HashSet<NetworkIdentity> observing => ((NetworkConnectionToClient)this).observing;
  15. /// <summary>Unique identifier for this connection that is assigned by the transport layer.</summary>
  16. // assigned by transport, this id is unique for every connection on server.
  17. // clients don't know their own id and they don't know other client's ids.
  18. public readonly int connectionId;
  19. /// <summary>Flag that indicates the client has been authenticated.</summary>
  20. public bool isAuthenticated;
  21. /// <summary>General purpose object to hold authentication data, character selection, tokens, etc.</summary>
  22. public object authenticationData;
  23. /// <summary>A server connection is ready after joining the game world.</summary>
  24. // TODO move this to ConnectionToClient so the flag only lives on server
  25. // connections? clients could use NetworkClient.ready to avoid redundant
  26. // state.
  27. public bool isReady;
  28. /// <summary>IP address of the connection. Can be useful for game master IP bans etc.</summary>
  29. public abstract string address { get; }
  30. /// <summary>Last time a message was received for this connection. Includes system and user messages.</summary>
  31. public float lastMessageTime;
  32. /// <summary>This connection's main object (usually the player object).</summary>
  33. public NetworkIdentity identity { get; internal set; }
  34. // DEPRECATED 2022-02-05
  35. [Obsolete("Cast to NetworkConnectionToClient to access .owned")]
  36. public HashSet<NetworkIdentity> clientOwnedObjects => owned;
  37. /// <summary>All NetworkIdentities owned by this connection. Can be main player, pets, etc.</summary>
  38. // .owned is now valid both on server and on client.
  39. // IMPORTANT: this needs to be <NetworkIdentity>, not <uint netId>.
  40. // fixes a bug where DestroyOwnedObjects wouldn't find the
  41. // netId anymore: https://github.com/vis2k/Mirror/issues/1380
  42. // Works fine with NetworkIdentity pointers though.
  43. public readonly HashSet<NetworkIdentity> owned = new HashSet<NetworkIdentity>();
  44. // batching from server to client & client to server.
  45. // fewer transport calls give us significantly better performance/scale.
  46. //
  47. // for a 64KB max message transport and 64 bytes/message on average, we
  48. // reduce transport calls by a factor of 1000.
  49. //
  50. // depending on the transport, this can give 10x performance.
  51. //
  52. // Dictionary<channelId, batch> because we have multiple channels.
  53. protected Dictionary<int, Batcher> batches = new Dictionary<int, Batcher>();
  54. /// <summary>last batch's remote timestamp. not interpolated. useful for NetworkTransform etc.</summary>
  55. // for any given NetworkMessage/Rpc/Cmd/OnSerialize, this was the time
  56. // on the REMOTE END when it was sent.
  57. //
  58. // NOTE: this is NOT in NetworkTime, it needs to be per-connection
  59. // because the server receives different batch timestamps from
  60. // different connections.
  61. public double remoteTimeStamp { get; internal set; }
  62. internal NetworkConnection()
  63. {
  64. // set lastTime to current time when creating connection to make
  65. // sure it isn't instantly kicked for inactivity
  66. lastMessageTime = Time.time;
  67. }
  68. internal NetworkConnection(int networkConnectionId) : this()
  69. {
  70. connectionId = networkConnectionId;
  71. }
  72. // TODO if we only have Reliable/Unreliable, then we could initialize
  73. // two batches and avoid this code
  74. protected Batcher GetBatchForChannelId(int channelId)
  75. {
  76. // get existing or create new writer for the channelId
  77. Batcher batch;
  78. if (!batches.TryGetValue(channelId, out batch))
  79. {
  80. // get max batch size for this channel
  81. int threshold = Transport.active.GetBatchThreshold(channelId);
  82. // create batcher
  83. batch = new Batcher(threshold);
  84. batches[channelId] = batch;
  85. }
  86. return batch;
  87. }
  88. // validate packet size before sending. show errors if too big/small.
  89. // => it's best to check this here, we can't assume that all transports
  90. // would check max size and show errors internally. best to do it
  91. // in one place in Mirror.
  92. // => it's important to log errors, so the user knows what went wrong.
  93. protected static bool ValidatePacketSize(ArraySegment<byte> segment, int channelId)
  94. {
  95. int max = Transport.active.GetMaxPacketSize(channelId);
  96. if (segment.Count > max)
  97. {
  98. Debug.LogError($"NetworkConnection.ValidatePacketSize: cannot send packet larger than {max} bytes, was {segment.Count} bytes");
  99. return false;
  100. }
  101. if (segment.Count == 0)
  102. {
  103. // zero length packets getting into the packet queues are bad.
  104. Debug.LogError("NetworkConnection.ValidatePacketSize: cannot send zero bytes");
  105. return false;
  106. }
  107. // good size
  108. return true;
  109. }
  110. // Send stage one: NetworkMessage<T>
  111. /// <summary>Send a NetworkMessage to this connection over the given channel.</summary>
  112. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  113. public void Send<T>(T message, int channelId = Channels.Reliable)
  114. where T : struct, NetworkMessage
  115. {
  116. using (NetworkWriterPooled writer = NetworkWriterPool.Get())
  117. {
  118. // pack message and send allocation free
  119. NetworkMessages.Pack(message, writer);
  120. NetworkDiagnostics.OnSend(message, channelId, writer.Position, 1);
  121. Send(writer.ToArraySegment(), channelId);
  122. }
  123. }
  124. // Send stage two: serialized NetworkMessage as ArraySegment<byte>
  125. // internal because no one except Mirror should send bytes directly to
  126. // the client. they would be detected as a message. send messages instead.
  127. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  128. internal virtual void Send(ArraySegment<byte> segment, int channelId = Channels.Reliable)
  129. {
  130. //Debug.Log($"ConnectionSend {this} bytes:{BitConverter.ToString(segment.Array, segment.Offset, segment.Count)}");
  131. // add to batch no matter what.
  132. // batching will try to fit as many as possible into MTU.
  133. // but we still allow > MTU, e.g. kcp max packet size 144kb.
  134. // those are simply sent as single batches.
  135. //
  136. // IMPORTANT: do NOT send > batch sized messages directly:
  137. // - data race: large messages would be sent directly. small
  138. // messages would be sent in the batch at the end of frame
  139. // - timestamps: if batching assumes a timestamp, then large
  140. // messages need that too.
  141. //
  142. // NOTE: we ALWAYS batch. it's not optional, because the
  143. // receiver needs timestamps for NT etc.
  144. //
  145. // NOTE: we do NOT ValidatePacketSize here yet. the final packet
  146. // will be the full batch, including timestamp.
  147. GetBatchForChannelId(channelId).AddMessage(segment, NetworkTime.localTime);
  148. }
  149. // Send stage three: hand off to transport
  150. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  151. protected abstract void SendToTransport(ArraySegment<byte> segment, int channelId = Channels.Reliable);
  152. // flush batched messages at the end of every Update.
  153. internal virtual void Update()
  154. {
  155. // go through batches for all channels
  156. // foreach ((int key, Batcher batcher) in batches) // Unity 2020 doesn't support deconstruct yet
  157. foreach (KeyValuePair<int, Batcher> kvp in batches)
  158. {
  159. // make and send as many batches as necessary from the stored
  160. // messages.
  161. using (NetworkWriterPooled writer = NetworkWriterPool.Get())
  162. {
  163. // make a batch with our local time (double precision)
  164. while (kvp.Value.GetBatch(writer))
  165. {
  166. // validate packet before handing the batch to the
  167. // transport. this guarantees that we always stay
  168. // within transport's max message size limit.
  169. // => just in case transport forgets to check it
  170. // => just in case mirror miscalulated it etc.
  171. ArraySegment<byte> segment = writer.ToArraySegment();
  172. if (ValidatePacketSize(segment, kvp.Key))
  173. {
  174. // send to transport
  175. SendToTransport(segment, kvp.Key);
  176. //UnityEngine.Debug.Log($"sending batch of {writer.Position} bytes for channel={kvp.Key} connId={connectionId}");
  177. // reset writer for each new batch
  178. writer.Position = 0;
  179. }
  180. }
  181. }
  182. }
  183. }
  184. /// <summary>Check if we received a message within the last 'timeout' seconds.</summary>
  185. internal virtual bool IsAlive(float timeout) => Time.time - lastMessageTime < timeout;
  186. /// <summary>Disconnects this connection.</summary>
  187. // for future reference, here is how Disconnects work in Mirror.
  188. //
  189. // first, there are two types of disconnects:
  190. // * voluntary: the other end simply disconnected
  191. // * involuntary: server disconnects a client by itself
  192. //
  193. // UNET had special (complex) code to handle both cases differently.
  194. //
  195. // Mirror handles both cases the same way:
  196. // * Disconnect is called from TOP to BOTTOM
  197. // NetworkServer/Client -> NetworkConnection -> Transport.Disconnect()
  198. // * Disconnect is handled from BOTTOM to TOP
  199. // Transport.OnDisconnected -> ...
  200. //
  201. // in other words, calling Disconnect() does no cleanup whatsoever.
  202. // it simply asks the transport to disconnect.
  203. // then later the transport events will do the clean up.
  204. public abstract void Disconnect();
  205. public override string ToString() => $"connection({connectionId})";
  206. }
  207. }