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