NetworkConnection.cs 9.7 KB

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