Mirror Networking
NetworkWriterExtensions.cs
1using System;
2using System.Collections.Generic;
3using System.Runtime.InteropServices;
4using System.Text;
5using UnityEngine;
6
7namespace Mirror
8{
9 // Mirror's Weaver automatically detects all NetworkWriter function types,
10 // but they do all need to be extensions.
11 public static class NetworkWriterExtensions
12 {
13 // cache encoding instead of creating it with BinaryWriter 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 static readonly byte[] stringBuffer = new byte[NetworkWriter.MaxStringLength];
18
19 public static void WriteByte(this NetworkWriter writer, byte value) => writer.WriteBlittable(value);
20 public static void WriteByteNullable(this NetworkWriter writer, byte? value) => writer.WriteBlittableNullable(value);
21
22 public static void WriteSByte(this NetworkWriter writer, sbyte value) => writer.WriteBlittable(value);
23 public static void WriteSByteNullable(this NetworkWriter writer, sbyte? value) => writer.WriteBlittableNullable(value);
24
25 // char is not blittable. convert to ushort.
26 public static void WriteChar(this NetworkWriter writer, char value) => writer.WriteBlittable((ushort)value);
27 public static void WriteCharNullable(this NetworkWriter writer, char? value) => writer.WriteBlittableNullable((ushort?)value);
28
29 // bool is not blittable. convert to byte.
30 public static void WriteBool(this NetworkWriter writer, bool value) => writer.WriteBlittable((byte)(value ? 1 : 0));
31 public static void WriteBoolNullable(this NetworkWriter writer, bool? value) => writer.WriteBlittableNullable(value.HasValue ? ((byte)(value.Value ? 1 : 0)) : new byte?());
32
33 public static void WriteShort(this NetworkWriter writer, short value) => writer.WriteBlittable(value);
34 public static void WriteShortNullable(this NetworkWriter writer, short? value) => writer.WriteBlittableNullable(value);
35
36 public static void WriteUShort(this NetworkWriter writer, ushort value) => writer.WriteBlittable(value);
37 public static void WriteUShortNullable(this NetworkWriter writer, ushort? value) => writer.WriteBlittableNullable(value);
38
39 public static void WriteInt(this NetworkWriter writer, int value) => writer.WriteBlittable(value);
40 public static void WriteIntNullable(this NetworkWriter writer, int? value) => writer.WriteBlittableNullable(value);
41
42 public static void WriteUInt(this NetworkWriter writer, uint value) => writer.WriteBlittable(value);
43 public static void WriteUIntNullable(this NetworkWriter writer, uint? value) => writer.WriteBlittableNullable(value);
44
45 public static void WriteLong(this NetworkWriter writer, long value) => writer.WriteBlittable(value);
46 public static void WriteLongNullable(this NetworkWriter writer, long? value) => writer.WriteBlittableNullable(value);
47
48 public static void WriteULong(this NetworkWriter writer, ulong value) => writer.WriteBlittable(value);
49 public static void WriteULongNullable(this NetworkWriter writer, ulong? value) => writer.WriteBlittableNullable(value);
50
51 public static void WriteFloat(this NetworkWriter writer, float value) => writer.WriteBlittable(value);
52 public static void WriteFloatNullable(this NetworkWriter writer, float? value) => writer.WriteBlittableNullable(value);
53
54 [StructLayout(LayoutKind.Explicit)]
55 internal struct UIntDouble
56 {
57 [FieldOffset(0)]
58 public double doubleValue;
59
60 [FieldOffset(0)]
61 public ulong longValue;
62 }
63
64 public static void WriteDouble(this NetworkWriter writer, double value)
65 {
66 // DEBUG: try to find the exact value that fails.
67 //UIntDouble convert = new UIntDouble{doubleValue = value};
68 //Debug.Log($"=> NetworkWriter.WriteDouble: {value} => 0x{convert.longValue:X8}");
69
70
71 writer.WriteBlittable(value);
72 }
73 public static void WriteDoubleNullable(this NetworkWriter writer, double? value) => writer.WriteBlittableNullable(value);
74
75 public static void WriteDecimal(this NetworkWriter writer, decimal value) => writer.WriteBlittable(value);
76 public static void WriteDecimalNullable(this NetworkWriter writer, decimal? value) => writer.WriteBlittableNullable(value);
77
78 public static void WriteString(this NetworkWriter writer, string value)
79 {
80 // write 0 for null support, increment real size by 1
81 // (note: original HLAPI would write "" for null strings, but if a
82 // string is null on the server then it should also be null
83 // on the client)
84 if (value == null)
85 {
86 writer.WriteUShort(0);
87 return;
88 }
89
90 // write string with same method as NetworkReader
91 // convert to byte[]
92 int size = encoding.GetBytes(value, 0, value.Length, stringBuffer, 0);
93
94 // check if within max size
95 if (size >= NetworkWriter.MaxStringLength)
96 {
97 throw new IndexOutOfRangeException($"NetworkWriter.Write(string) too long: {size}. Limit: {NetworkWriter.MaxStringLength}");
98 }
99
100 // write size and bytes
101 writer.WriteUShort(checked((ushort)(size + 1)));
102 writer.WriteBytes(stringBuffer, 0, size);
103 }
104
105 public static void WriteBytesAndSizeSegment(this NetworkWriter writer, ArraySegment<byte> buffer)
106 {
107 writer.WriteBytesAndSize(buffer.Array, buffer.Offset, buffer.Count);
108 }
109
110 // Weaver needs a write function with just one byte[] parameter
111 // (we don't name it .Write(byte[]) because it's really a WriteBytesAndSize since we write size / null info too)
112
113 public static void WriteBytesAndSize(this NetworkWriter writer, byte[] buffer)
114 {
115 // buffer might be null, so we can't use .Length in that case
116 writer.WriteBytesAndSize(buffer, 0, buffer != null ? buffer.Length : 0);
117 }
118
119 // for byte arrays with dynamic size, where the reader doesn't know how many will come
120 // (like an inventory with different items etc.)
121
122 public static void WriteBytesAndSize(this NetworkWriter writer, byte[] buffer, int offset, int count)
123 {
124 // null is supported because [SyncVar]s might be structs with null byte[] arrays
125 // write 0 for null array, increment normal size by 1 to save bandwidth
126 // (using size=-1 for null would limit max size to 32kb instead of 64kb)
127 if (buffer == null)
128 {
129 writer.WriteUInt(0u);
130 return;
131 }
132 writer.WriteUInt(checked((uint)count) + 1u);
133 writer.WriteBytes(buffer, offset, count);
134 }
135
136 public static void WriteArraySegment<T>(this NetworkWriter writer, ArraySegment<T> segment)
137 {
138 int length = segment.Count;
139 writer.WriteInt(length);
140 for (int i = 0; i < length; i++)
141 {
142 writer.Write(segment.Array[segment.Offset + i]);
143 }
144 }
145
146 public static void WriteVector2(this NetworkWriter writer, Vector2 value) => writer.WriteBlittable(value);
147 public static void WriteVector2Nullable(this NetworkWriter writer, Vector2? value) => writer.WriteBlittableNullable(value);
148
149 public static void WriteVector3(this NetworkWriter writer, Vector3 value) => writer.WriteBlittable(value);
150 public static void WriteVector3Nullable(this NetworkWriter writer, Vector3? value) => writer.WriteBlittableNullable(value);
151
152 public static void WriteVector4(this NetworkWriter writer, Vector4 value) => writer.WriteBlittable(value);
153 public static void WriteVector4Nullable(this NetworkWriter writer, Vector4? value) => writer.WriteBlittableNullable(value);
154
155 public static void WriteVector2Int(this NetworkWriter writer, Vector2Int value) => writer.WriteBlittable(value);
156 public static void WriteVector2IntNullable(this NetworkWriter writer, Vector2Int? value) => writer.WriteBlittableNullable(value);
157
158 public static void WriteVector3Int(this NetworkWriter writer, Vector3Int value) => writer.WriteBlittable(value);
159 public static void WriteVector3IntNullable(this NetworkWriter writer, Vector3Int? value) => writer.WriteBlittableNullable(value);
160
161 public static void WriteColor(this NetworkWriter writer, Color value) => writer.WriteBlittable(value);
162 public static void WriteColorNullable(this NetworkWriter writer, Color? value) => writer.WriteBlittableNullable(value);
163
164 public static void WriteColor32(this NetworkWriter writer, Color32 value) => writer.WriteBlittable(value);
165 public static void WriteColor32Nullable(this NetworkWriter writer, Color32? value) => writer.WriteBlittableNullable(value);
166
167 public static void WriteQuaternion(this NetworkWriter writer, Quaternion value) => writer.WriteBlittable(value);
168 public static void WriteQuaternionNullable(this NetworkWriter writer, Quaternion? value) => writer.WriteBlittableNullable(value);
169
170 public static void WriteRect(this NetworkWriter writer, Rect value) => writer.WriteBlittable(value);
171 public static void WriteRectNullable(this NetworkWriter writer, Rect? value) => writer.WriteBlittableNullable(value);
172
173 public static void WritePlane(this NetworkWriter writer, Plane value) => writer.WriteBlittable(value);
174 public static void WritePlaneNullable(this NetworkWriter writer, Plane? value) => writer.WriteBlittableNullable(value);
175
176 public static void WriteRay(this NetworkWriter writer, Ray value) => writer.WriteBlittable(value);
177 public static void WriteRayNullable(this NetworkWriter writer, Ray? value) => writer.WriteBlittableNullable(value);
178
179 public static void WriteMatrix4x4(this NetworkWriter writer, Matrix4x4 value) => writer.WriteBlittable(value);
180 public static void WriteMatrix4x4Nullable(this NetworkWriter writer, Matrix4x4? value) => writer.WriteBlittableNullable(value);
181
182 public static void WriteGuid(this NetworkWriter writer, Guid value)
183 {
184 byte[] data = value.ToByteArray();
185 writer.WriteBytes(data, 0, data.Length);
186 }
187 public static void WriteGuidNullable(this NetworkWriter writer, Guid? value)
188 {
189 writer.WriteBool(value.HasValue);
190 if (value.HasValue)
191 writer.WriteGuid(value.Value);
192 }
193
194 public static void WriteNetworkIdentity(this NetworkWriter writer, NetworkIdentity value)
195 {
196 if (value == null)
197 {
198 writer.WriteUInt(0);
199 return;
200 }
201
202 // users might try to use unspawned / prefab GameObjects in
203 // rpcs/cmds/syncvars/messages. they would be null on the other
204 // end, and it might not be obvious why. let's make it obvious.
205 // https://github.com/vis2k/Mirror/issues/2060
206 //
207 // => warning (instead of exception) because we also use a warning
208 // if a GameObject doesn't have a NetworkIdentity component etc.
209 if (value.netId == 0)
210 Debug.LogWarning($"Attempted to serialize unspawned GameObject: {value.name}. Prefabs and unspawned GameObjects would always be null on the other side. Please spawn it before using it in [SyncVar]s/Rpcs/Cmds/NetworkMessages etc.");
211
212 writer.WriteUInt(value.netId);
213 }
214
215 public static void WriteNetworkBehaviour(this NetworkWriter writer, NetworkBehaviour value)
216 {
217 if (value == null)
218 {
219 writer.WriteUInt(0);
220 return;
221 }
222 writer.WriteUInt(value.netId);
223 writer.WriteByte((byte)value.ComponentIndex);
224 }
225
226 public static void WriteTransform(this NetworkWriter writer, Transform value)
227 {
228 if (value == null)
229 {
230 writer.WriteUInt(0);
231 return;
232 }
233 NetworkIdentity identity = value.GetComponent<NetworkIdentity>();
234 if (identity != null)
235 {
236 writer.WriteUInt(identity.netId);
237 }
238 else
239 {
240 Debug.LogWarning($"NetworkWriter {value} has no NetworkIdentity");
241 writer.WriteUInt(0);
242 }
243 }
244
245 public static void WriteGameObject(this NetworkWriter writer, GameObject value)
246 {
247 if (value == null)
248 {
249 writer.WriteUInt(0);
250 return;
251 }
252
253 // warn if the GameObject doesn't have a NetworkIdentity,
254 NetworkIdentity identity = value.GetComponent<NetworkIdentity>();
255 if (identity == null)
256 Debug.LogWarning($"NetworkWriter {value} has no NetworkIdentity");
257
258 // serialize the correct amount of data in any case to make sure
259 // that the other end can read the expected amount of data too.
260 writer.WriteNetworkIdentity(identity);
261 }
262
263 public static void WriteList<T>(this NetworkWriter writer, List<T> list)
264 {
265 if (list is null)
266 {
267 writer.WriteInt(-1);
268 return;
269 }
270 writer.WriteInt(list.Count);
271 for (int i = 0; i < list.Count; i++)
272 writer.Write(list[i]);
273 }
274
275 public static void WriteArray<T>(this NetworkWriter writer, T[] array)
276 {
277 if (array is null)
278 {
279 writer.WriteInt(-1);
280 return;
281 }
282 writer.WriteInt(array.Length);
283 for (int i = 0; i < array.Length; i++)
284 writer.Write(array[i]);
285 }
286
287 public static void WriteUri(this NetworkWriter writer, Uri uri)
288 {
289 writer.WriteString(uri?.ToString());
290 }
291
292 public static void WriteTexture2D(this NetworkWriter writer, Texture2D texture2D)
293 {
294 // TODO allocation protection when sending textures to server.
295 // currently can allocate 32k x 32k x 4 byte = 3.8 GB
296
297 // support 'null' textures for [SyncVar]s etc.
298 // https://github.com/vis2k/Mirror/issues/3144
299 // simply send -1 for width.
300 if (texture2D == null)
301 {
302 writer.WriteShort(-1);
303 return;
304 }
305
306 // write dimensions first so reader can create the texture with size
307 // 32k x 32k short is more than enough
308 writer.WriteShort((short)texture2D.width);
309 writer.WriteShort((short)texture2D.height);
310 writer.WriteArray(texture2D.GetPixels32());
311 }
312
313 public static void WriteSprite(this NetworkWriter writer, Sprite sprite)
314 {
315 // support 'null' textures for [SyncVar]s etc.
316 // https://github.com/vis2k/Mirror/issues/3144
317 // simply send a 'null' for texture content.
318 if (sprite == null)
319 {
320 writer.WriteTexture2D(null);
321 return;
322 }
323
324 writer.WriteTexture2D(sprite.texture);
325 writer.WriteRect(sprite.rect);
326 writer.WriteVector2(sprite.pivot);
327 }
328 }
329}
Base class for networked components.
int ComponentIndex
Returns the index of the component on this object
uint netId
The unique network Id of this object (unique at runtime).
NetworkIdentity identifies objects across the network.
uint netId
The unique network Id of this object (unique at runtime).
Network Writer for most simple types like floats, ints, buffers, structs, etc. Use NetworkWriterPool....