Mirror Networking
NetworkReaderExtensions.cs
1using System;
2using System.Collections.Generic;
3using System.IO;
4using System.Text;
5using UnityEngine;
6
7namespace Mirror
8{
9 // Mirror's Weaver automatically detects all NetworkReader function types,
10 // but they do all need to be extensions.
11 public static class NetworkReaderExtensions
12 {
13 // cache encoding instead of creating it each time
14 // 1000 readers before: 1MB GC, 30ms
15 // 1000 readers after: 0.8MB GC, 18ms
16 static readonly UTF8Encoding encoding = new UTF8Encoding(false, true);
17
18 public static byte ReadByte(this NetworkReader reader) => reader.ReadBlittable<byte>();
19 public static byte? ReadByteNullable(this NetworkReader reader) => reader.ReadBlittableNullable<byte>();
20
21 public static sbyte ReadSByte(this NetworkReader reader) => reader.ReadBlittable<sbyte>();
22 public static sbyte? ReadSByteNullable(this NetworkReader reader) => reader.ReadBlittableNullable<sbyte>();
23
24 // bool is not blittable. read as ushort.
25 public static char ReadChar(this NetworkReader reader) => (char)reader.ReadBlittable<ushort>();
26 public static char? ReadCharNullable(this NetworkReader reader) => (char?)reader.ReadBlittableNullable<ushort>();
27
28 // bool is not blittable. read as byte.
29 public static bool ReadBool(this NetworkReader reader) => reader.ReadBlittable<byte>() != 0;
30 public static bool? ReadBoolNullable(this NetworkReader reader)
31 {
32 byte? value = reader.ReadBlittableNullable<byte>();
33 return value.HasValue ? (value.Value != 0) : default(bool?);
34 }
35
36 public static short ReadShort(this NetworkReader reader) => (short)reader.ReadUShort();
37 public static short? ReadShortNullable(this NetworkReader reader) => reader.ReadBlittableNullable<short>();
38
39 public static ushort ReadUShort(this NetworkReader reader) => reader.ReadBlittable<ushort>();
40 public static ushort? ReadUShortNullable(this NetworkReader reader) => reader.ReadBlittableNullable<ushort>();
41
42 public static int ReadInt(this NetworkReader reader) => reader.ReadBlittable<int>();
43 public static int? ReadIntNullable(this NetworkReader reader) => reader.ReadBlittableNullable<int>();
44
45 public static uint ReadUInt(this NetworkReader reader) => reader.ReadBlittable<uint>();
46 public static uint? ReadUIntNullable(this NetworkReader reader) => reader.ReadBlittableNullable<uint>();
47
48 public static long ReadLong(this NetworkReader reader) => reader.ReadBlittable<long>();
49 public static long? ReadLongNullable(this NetworkReader reader) => reader.ReadBlittableNullable<long>();
50
51 public static ulong ReadULong(this NetworkReader reader) => reader.ReadBlittable<ulong>();
52 public static ulong? ReadULongNullable(this NetworkReader reader) => reader.ReadBlittableNullable<ulong>();
53
54 public static float ReadFloat(this NetworkReader reader) => reader.ReadBlittable<float>();
55 public static float? ReadFloatNullable(this NetworkReader reader) => reader.ReadBlittableNullable<float>();
56
57 public static double ReadDouble(this NetworkReader reader) => reader.ReadBlittable<double>();
58 public static double? ReadDoubleNullable(this NetworkReader reader) => reader.ReadBlittableNullable<double>();
59
60 public static decimal ReadDecimal(this NetworkReader reader) => reader.ReadBlittable<decimal>();
61 public static decimal? ReadDecimalNullable(this NetworkReader reader) => reader.ReadBlittableNullable<decimal>();
62
64 public static string ReadString(this NetworkReader reader)
65 {
66 // read number of bytes
67 ushort size = reader.ReadUShort();
68
69 // null support, see NetworkWriter
70 if (size == 0)
71 return null;
72
73 int realSize = size - 1;
74
75 // make sure it's within limits to avoid allocation attacks etc.
76 if (realSize >= NetworkWriter.MaxStringLength)
77 {
78 throw new EndOfStreamException($"ReadString too long: {realSize}. Limit is: {NetworkWriter.MaxStringLength}");
79 }
80
81 ArraySegment<byte> data = reader.ReadBytesSegment(realSize);
82
83 // convert directly from buffer to string via encoding
84 return encoding.GetString(data.Array, data.Offset, data.Count);
85 }
86
88 public static byte[] ReadBytesAndSize(this NetworkReader reader)
89 {
90 // count = 0 means the array was null
91 // otherwise count -1 is the length of the array
92 uint count = reader.ReadUInt();
93 // Use checked() to force it to throw OverflowException if data is invalid
94 return count == 0 ? null : reader.ReadBytes(checked((int)(count - 1u)));
95 }
96
97 public static byte[] ReadBytes(this NetworkReader reader, int count)
98 {
99 byte[] bytes = new byte[count];
100 reader.ReadBytes(bytes, count);
101 return bytes;
102 }
103
105 public static ArraySegment<byte> ReadBytesAndSizeSegment(this NetworkReader reader)
106 {
107 // count = 0 means the array was null
108 // otherwise count - 1 is the length of the array
109 uint count = reader.ReadUInt();
110 // Use checked() to force it to throw OverflowException if data is invalid
111 return count == 0 ? default : reader.ReadBytesSegment(checked((int)(count - 1u)));
112 }
113
114 public static Vector2 ReadVector2(this NetworkReader reader) => reader.ReadBlittable<Vector2>();
115 public static Vector2? ReadVector2Nullable(this NetworkReader reader) => reader.ReadBlittableNullable<Vector2>();
116
117 public static Vector3 ReadVector3(this NetworkReader reader) => reader.ReadBlittable<Vector3>();
118 public static Vector3? ReadVector3Nullable(this NetworkReader reader) => reader.ReadBlittableNullable<Vector3>();
119
120 public static Vector4 ReadVector4(this NetworkReader reader) => reader.ReadBlittable<Vector4>();
121 public static Vector4? ReadVector4Nullable(this NetworkReader reader) => reader.ReadBlittableNullable<Vector4>();
122
123 public static Vector2Int ReadVector2Int(this NetworkReader reader) => reader.ReadBlittable<Vector2Int>();
124 public static Vector2Int? ReadVector2IntNullable(this NetworkReader reader) => reader.ReadBlittableNullable<Vector2Int>();
125
126 public static Vector3Int ReadVector3Int(this NetworkReader reader) => reader.ReadBlittable<Vector3Int>();
127 public static Vector3Int? ReadVector3IntNullable(this NetworkReader reader) => reader.ReadBlittableNullable<Vector3Int>();
128
129 public static Color ReadColor(this NetworkReader reader) => reader.ReadBlittable<Color>();
130 public static Color? ReadColorNullable(this NetworkReader reader) => reader.ReadBlittableNullable<Color>();
131
132 public static Color32 ReadColor32(this NetworkReader reader) => reader.ReadBlittable<Color32>();
133 public static Color32? ReadColor32Nullable(this NetworkReader reader) => reader.ReadBlittableNullable<Color32>();
134
135 public static Quaternion ReadQuaternion(this NetworkReader reader) => reader.ReadBlittable<Quaternion>();
136 public static Quaternion? ReadQuaternionNullable(this NetworkReader reader) => reader.ReadBlittableNullable<Quaternion>();
137
138 public static Rect ReadRect(this NetworkReader reader) => reader.ReadBlittable<Rect>();
139 public static Rect? ReadRectNullable(this NetworkReader reader) => reader.ReadBlittableNullable<Rect>();
140
141 public static Plane ReadPlane(this NetworkReader reader) => reader.ReadBlittable<Plane>();
142 public static Plane? ReadPlaneNullable(this NetworkReader reader) => reader.ReadBlittableNullable<Plane>();
143
144 public static Ray ReadRay(this NetworkReader reader) => reader.ReadBlittable<Ray>();
145 public static Ray? ReadRayNullable(this NetworkReader reader) => reader.ReadBlittableNullable<Ray>();
146
147 public static Matrix4x4 ReadMatrix4x4(this NetworkReader reader)=> reader.ReadBlittable<Matrix4x4>();
148 public static Matrix4x4? ReadMatrix4x4Nullable(this NetworkReader reader) => reader.ReadBlittableNullable<Matrix4x4>();
149
150 public static Guid ReadGuid(this NetworkReader reader) => new Guid(reader.ReadBytes(16));
151 public static Guid? ReadGuidNullable(this NetworkReader reader) => reader.ReadBool() ? ReadGuid(reader) : default(Guid?);
152
153 public static NetworkIdentity ReadNetworkIdentity(this NetworkReader reader)
154 {
155 uint netId = reader.ReadUInt();
156 if (netId == 0)
157 return null;
158
159 // NOTE: a netId not being in spawned is common.
160 // for example, "[SyncVar] NetworkIdentity target" netId would not
161 // be known on client if the monster walks out of proximity for a
162 // moment. no need to log any error or warning here.
163 return Utils.GetSpawnedInServerOrClient(netId);
164 }
165
166 public static NetworkBehaviour ReadNetworkBehaviour(this NetworkReader reader)
167 {
168 // read netId first.
169 //
170 // IMPORTANT: if netId != 0, writer always writes componentIndex.
171 // reusing ReadNetworkIdentity() might return a null NetworkIdentity
172 // even if netId was != 0 but the identity disappeared on the client,
173 // resulting in unequal amounts of data being written / read.
174 // https://github.com/vis2k/Mirror/issues/2972
175 uint netId = reader.ReadUInt();
176 if (netId == 0)
177 return null;
178
179 // read component index in any case, BEFORE searching the spawned
180 // NetworkIdentity by netId.
181 byte componentIndex = reader.ReadByte();
182
183 // NOTE: a netId not being in spawned is common.
184 // for example, "[SyncVar] NetworkIdentity target" netId would not
185 // be known on client if the monster walks out of proximity for a
186 // moment. no need to log any error or warning here.
187 NetworkIdentity identity = Utils.GetSpawnedInServerOrClient(netId);
188
189 return identity != null
190 ? identity.NetworkBehaviours[componentIndex]
191 : null;
192 }
193
194 public static T ReadNetworkBehaviour<T>(this NetworkReader reader) where T : NetworkBehaviour
195 {
196 return reader.ReadNetworkBehaviour() as T;
197 }
198
199 public static NetworkBehaviour.NetworkBehaviourSyncVar ReadNetworkBehaviourSyncVar(this NetworkReader reader)
200 {
201 uint netId = reader.ReadUInt();
202 byte componentIndex = default;
203
204 // if netId is not 0, then index is also sent to read before returning
205 if (netId != 0)
206 {
207 componentIndex = reader.ReadByte();
208 }
209
210 return new NetworkBehaviour.NetworkBehaviourSyncVar(netId, componentIndex);
211 }
212
213 public static Transform ReadTransform(this NetworkReader reader)
214 {
215 // Don't use null propagation here as it could lead to MissingReferenceException
216 NetworkIdentity networkIdentity = reader.ReadNetworkIdentity();
217 return networkIdentity != null ? networkIdentity.transform : null;
218 }
219
220 public static GameObject ReadGameObject(this NetworkReader reader)
221 {
222 // Don't use null propagation here as it could lead to MissingReferenceException
223 NetworkIdentity networkIdentity = reader.ReadNetworkIdentity();
224 return networkIdentity != null ? networkIdentity.gameObject : null;
225 }
226
227 public static List<T> ReadList<T>(this NetworkReader reader)
228 {
229 int length = reader.ReadInt();
230 if (length < 0)
231 return null;
232 List<T> result = new List<T>(length);
233 for (int i = 0; i < length; i++)
234 {
235 result.Add(reader.Read<T>());
236 }
237 return result;
238 }
239
240 public static T[] ReadArray<T>(this NetworkReader reader)
241 {
242 int length = reader.ReadInt();
243
244 // we write -1 for null
245 if (length < 0)
246 return null;
247
248 // todo throw an exception for other negative values (we never write them, likely to be attacker)
249
250 // this assumes that a reader for T reads at least 1 bytes
251 // we can't know the exact size of T because it could have a user created reader
252 // NOTE: don't add to length as it could overflow if value is int.max
253 if (length > reader.Length - reader.Position)
254 {
255 throw new EndOfStreamException($"Received array that is too large: {length}");
256 }
257
258 T[] result = new T[length];
259 for (int i = 0; i < length; i++)
260 {
261 result[i] = reader.Read<T>();
262 }
263 return result;
264 }
265
266 public static Uri ReadUri(this NetworkReader reader)
267 {
268 string uriString = reader.ReadString();
269 return (string.IsNullOrWhiteSpace(uriString) ? null : new Uri(uriString));
270 }
271
272 public static Texture2D ReadTexture2D(this NetworkReader reader)
273 {
274 // TODO allocation protection when sending textures to server.
275 // currently can allocate 32k x 32k x 4 byte = 3.8 GB
276
277 // support 'null' textures for [SyncVar]s etc.
278 // https://github.com/vis2k/Mirror/issues/3144
279 short width = reader.ReadShort();
280 if (width == -1) return null;
281
282 // read height
283 short height = reader.ReadShort();
284 Texture2D texture2D = new Texture2D(width, height);
285
286 // read pixel content
287 Color32[] pixels = reader.ReadArray<Color32>();
288 texture2D.SetPixels32(pixels);
289 texture2D.Apply();
290 return texture2D;
291 }
292
293 public static Sprite ReadSprite(this NetworkReader reader)
294 {
295 // support 'null' textures for [SyncVar]s etc.
296 // https://github.com/vis2k/Mirror/issues/3144
297 Texture2D texture = reader.ReadTexture2D();
298 if (texture == null) return null;
299
300 // otherwise create a valid sprite
301 return Sprite.Create(texture, reader.ReadRect(), reader.ReadVector2());
302 }
303 }
304}
NetworkIdentity identifies objects across the network.
static ArraySegment< byte > ReadBytesAndSizeSegment(this NetworkReader reader)
static byte[] ReadBytesAndSize(this NetworkReader reader)
static string ReadString(this NetworkReader reader)
Network Reader for most simple types like floats, ints, buffers, structs, etc. Use NetworkReaderPool....
byte[] ReadBytes(byte[] bytes, int count)
Read 'count' bytes into the bytes array
ArraySegment< byte > ReadBytesSegment(int count)
Read 'count' bytes allocation-free as ArraySegment that points to the internal array.
Network Writer for most simple types like floats, ints, buffers, structs, etc. Use NetworkWriterPool....