Mirror Networking
MessagePacking.cs
1using System;
2using System.Runtime.CompilerServices;
3using UnityEngine;
4
5namespace Mirror
6{
7 // message packing all in one place, instead of constructing headers in all
8 // kinds of different places
9 //
10 // MsgType (2 bytes)
11 // Content (ContentSize bytes)
12 public static class MessagePacking
13 {
14 // message header size
15 public const int HeaderSize = sizeof(ushort);
16
17 // max message content size (without header) calculation for convenience
18 // -> Transport.GetMaxPacketSize is the raw maximum
19 // -> Every message gets serialized into <<id, content>>
20 // -> Every serialized message get put into a batch with a header
21 public static int MaxContentSize
22 {
23 [MethodImpl(MethodImplOptions.AggressiveInlining)]
25 - HeaderSize
26 - Batcher.HeaderSize;
27 }
28
29 // paul: 16 bits is enough to avoid collisions
30 // - keeps the message size small
31 // - in case of collisions, Mirror will display an error
32 [MethodImpl(MethodImplOptions.AggressiveInlining)]
33 public static ushort GetId<T>() where T : struct, NetworkMessage =>
34 (ushort)(typeof(T).FullName.GetStableHashCode() & 0xFFFF);
35
36 // pack message before sending
37 // -> NetworkWriter passed as arg so that we can use .ToArraySegment
38 // and do an allocation free send before recycling it.
39 [MethodImpl(MethodImplOptions.AggressiveInlining)]
40 public static void Pack<T>(T message, NetworkWriter writer)
41 where T : struct, NetworkMessage
42 {
43 ushort msgType = GetId<T>();
44 writer.WriteUShort(msgType);
45
46 // serialize message into writer
47 writer.Write(message);
48 }
49
50 // unpack message after receiving
51 // -> pass NetworkReader so it's less strange if we create it in here
52 // and pass it upwards.
53 // -> NetworkReader will point at content afterwards!
54 [MethodImpl(MethodImplOptions.AggressiveInlining)]
55 public static bool Unpack(NetworkReader messageReader, out ushort msgType)
56 {
57 // read message type
58 try
59 {
60 msgType = messageReader.ReadUShort();
61 return true;
62 }
63 catch (System.IO.EndOfStreamException)
64 {
65 msgType = 0;
66 return false;
67 }
68 }
69
70 // version for handlers with channelId
71 // inline! only exists for 20-30 messages and they call it all the time.
72 [MethodImpl(MethodImplOptions.AggressiveInlining)]
73 internal static NetworkMessageDelegate WrapHandler<T, C>(Action<C, T, int> handler, bool requireAuthentication)
74 where T : struct, NetworkMessage
75 where C : NetworkConnection
76 => (conn, reader, channelId) =>
77 {
78 // protect against DOS attacks if attackers try to send invalid
79 // data packets to crash the server/client. there are a thousand
80 // ways to cause an exception in data handling:
81 // - invalid headers
82 // - invalid message ids
83 // - invalid data causing exceptions
84 // - negative ReadBytesAndSize prefixes
85 // - invalid utf8 strings
86 // - etc.
87 //
88 // let's catch them all and then disconnect that connection to avoid
89 // further attacks.
90 T message = default;
91 // record start position for NetworkDiagnostics because reader might contain multiple messages if using batching
92 int startPos = reader.Position;
93 try
94 {
95 if (requireAuthentication && !conn.isAuthenticated)
96 {
97 // message requires authentication, but the connection was not authenticated
98 Debug.LogWarning($"Closing connection: {conn}. Received message {typeof(T)} that required authentication, but the user has not authenticated yet");
99 conn.Disconnect();
100 return;
101 }
102
103 //Debug.Log($"ConnectionRecv {conn} msgType:{typeof(T)} content:{BitConverter.ToString(reader.buffer.Array, reader.buffer.Offset, reader.buffer.Count)}");
104
105 // if it is a value type, just use default(T)
106 // otherwise allocate a new instance
107 message = reader.Read<T>();
108 }
109 catch (Exception exception)
110 {
111 Debug.LogError($"Closed connection: {conn}. This can happen if the other side accidentally (or an attacker intentionally) sent invalid data. Reason: {exception}");
112 conn.Disconnect();
113 return;
114 }
115 finally
116 {
117 int endPos = reader.Position;
118 // TODO: Figure out the correct channel
119 NetworkDiagnostics.OnReceive(message, channelId, endPos - startPos);
120 }
121
122 // user handler exception should not stop the whole server
123 try
124 {
125 // user implemented handler
126 handler((C)conn, message, channelId);
127 }
128 catch (Exception e)
129 {
130 Debug.LogError($"Disconnecting connId={conn.connectionId} to prevent exploits from an Exception in MessageHandler: {e.GetType().Name} {e.Message}\n{e.StackTrace}");
131 conn.Disconnect();
132 }
133 };
134
135 // version for handlers without channelId
136 // TODO obsolete this some day to always use the channelId version.
137 // all handlers in this version are wrapped with 1 extra action.
138 [MethodImpl(MethodImplOptions.AggressiveInlining)]
139 internal static NetworkMessageDelegate WrapHandler<T, C>(Action<C, T> handler, bool requireAuthentication)
140 where T : struct, NetworkMessage
141 where C : NetworkConnection
142 {
143 // wrap action as channelId version, call original
144 void Wrapped(C conn, T msg, int _) => handler(conn, msg);
145 return WrapHandler((Action<C, T, int>) Wrapped, requireAuthentication);
146 }
147 }
148}
Base NetworkConnection class for server-to-client and client-to-server connection.
abstract void Disconnect()
Disconnects this connection.
Profiling statistics for tool to subscribe to (profiler etc.)
Network Reader for most simple types like floats, ints, buffers, structs, etc. Use NetworkReaderPool....
Network Writer for most simple types like floats, ints, buffers, structs, etc. Use NetworkWriterPool....
Abstract transport layer component
Definition: Transport.cs:32
static Transport activeTransport
The current transport used by Mirror.
Definition: Transport.cs:35
abstract int GetMaxPacketSize(int channelId=Channels.Reliable)
Maximum message size for the given channel.