NetworkReaderExtensions.cs 18 KB

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