2021-12-27 22:30:22 -08:00
using System ;
using System.Collections.Generic ;
using System.Runtime.CompilerServices ;
using System.Text ;
using UnityEngine ;
namespace Mirror
{
/// <summary>Helper class that weaver populates with all writer types.</summary>
// Note that c# creates a different static variable for each type
// -> Weaver.ReaderWriterProcessor.InitializeReaderAndWriters() populates it
public static class Writer < T >
{
public static Action < NetworkWriter , T > write ;
}
/// <summary>Network Writer for most simple types like floats, ints, buffers, structs, etc. Use NetworkWriterPool.GetReader() to avoid allocations.</summary>
public class NetworkWriter
{
public const int MaxStringLength = 1024 * 32 ;
// create writer immediately with it's own buffer so no one can mess with it and so that we can resize it.
// note: BinaryWriter allocates too much, so we only use a MemoryStream
// => 1500 bytes by default because on average, most packets will be <= MTU
byte [ ] buffer = new byte [ 1500 ] ;
/// <summary>Next position to write to the buffer</summary>
public int Position ;
/// <summary>Reset both the position and length of the stream</summary>
// Leaves the capacity the same so that we can reuse this writer without
// extra allocations
public void Reset ( )
{
Position = 0 ;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
void EnsureCapacity ( int value )
{
if ( buffer . Length < value )
{
int capacity = Math . Max ( value , buffer . Length * 2 ) ;
Array . Resize ( ref buffer , capacity ) ;
}
}
/// <summary>Copies buffer until 'Position' to a new array.</summary>
public byte [ ] ToArray ( )
{
byte [ ] data = new byte [ Position ] ;
Array . ConstrainedCopy ( buffer , 0 , data , 0 , Position ) ;
return data ;
}
/// <summary>Returns allocation-free ArraySegment until 'Position'.</summary>
public ArraySegment < byte > ToArraySegment ( )
{
return new ArraySegment < byte > ( buffer , 0 , Position ) ;
}
2022-01-13 18:40:24 -08:00
// IMPORTANT: WriteBlittable<T> via fixed pinning WON'T WORK on android:
// https://github.com/vis2k/Mirror/issues/3044
// if we ever do it again, use NativeArray + .GetPtr()!
public void WriteByte ( byte value )
2021-12-27 22:30:22 -08:00
{
2022-01-13 18:40:24 -08:00
EnsureCapacity ( Position + 1 ) ;
buffer [ Position + + ] = value ;
2021-12-27 22:30:22 -08:00
}
// for byte arrays with consistent size, where the reader knows how many to read
// (like a packet opcode that's always the same)
public void WriteBytes ( byte [ ] buffer , int offset , int count )
{
EnsureCapacity ( Position + count ) ;
Array . ConstrainedCopy ( buffer , offset , this . buffer , Position , count ) ;
Position + = count ;
}
/// <summary>Writes any type that mirror supports. Uses weaver populated Writer(T).write.</summary>
public void Write < T > ( T value )
{
Action < NetworkWriter , T > writeDelegate = Writer < T > . write ;
if ( writeDelegate = = null )
{
Debug . LogError ( $"No writer found for {typeof(T)}. This happens either if you are missing a NetworkWriter extension for your custom type, or if weaving failed. Try to reimport a script to weave again." ) ;
}
else
{
writeDelegate ( this , value ) ;
}
}
}
// Mirror's Weaver automatically detects all NetworkWriter function types,
// but they do all need to be extensions.
public static class NetworkWriterExtensions
{
// cache encoding instead of creating it with BinaryWriter each time
// 1000 readers before: 1MB GC, 30ms
// 1000 readers after: 0.8MB GC, 18ms
static readonly UTF8Encoding encoding = new UTF8Encoding ( false , true ) ;
static readonly byte [ ] stringBuffer = new byte [ NetworkWriter . MaxStringLength ] ;
2022-01-13 18:40:24 -08:00
public static void WriteByte ( this NetworkWriter writer , byte value ) = > writer . WriteByte ( value ) ;
public static void WriteByteNullable ( this NetworkWriter writer , byte? value )
{
writer . WriteBool ( value . HasValue ) ;
if ( value . HasValue )
writer . WriteByte ( value . Value ) ;
}
2021-12-27 22:30:22 -08:00
2022-01-13 18:40:24 -08:00
public static void WriteSByte ( this NetworkWriter writer , sbyte value ) = > writer . WriteByte ( ( byte ) value ) ;
2021-12-27 22:30:22 -08:00
2022-01-13 18:40:24 -08:00
public static void WriteSByteNullable ( this NetworkWriter writer , sbyte? value )
{
writer . WriteBool ( value . HasValue ) ;
if ( value . HasValue )
writer . WriteSByte ( value . Value ) ;
}
public static void WriteChar ( this NetworkWriter writer , char value ) = > writer . WriteUShort ( value ) ;
2021-12-27 22:30:22 -08:00
2022-01-13 18:40:24 -08:00
public static void WriteCharNullable ( this NetworkWriter writer , char? value )
{
writer . WriteBool ( value . HasValue ) ;
if ( value . HasValue )
writer . WriteChar ( value . Value ) ;
}
2021-12-27 22:30:22 -08:00
2022-01-13 18:40:24 -08:00
public static void WriteBool ( this NetworkWriter writer , bool value ) = > writer . WriteByte ( ( byte ) ( value ? 1 : 0 ) ) ;
2021-12-27 22:30:22 -08:00
2022-01-13 18:40:24 -08:00
public static void WriteBoolNullable ( this NetworkWriter writer , bool? value )
{
writer . WriteBool ( value . HasValue ) ;
if ( value . HasValue )
writer . WriteBool ( value . Value ) ;
}
2021-12-27 22:30:22 -08:00
2022-01-13 18:40:24 -08:00
public static void WriteShort ( this NetworkWriter writer , short value ) = > writer . WriteUShort ( ( ushort ) value ) ;
2021-12-27 22:30:22 -08:00
2022-01-13 18:40:24 -08:00
public static void WriteShortNullable ( this NetworkWriter writer , short? value )
{
writer . WriteBool ( value . HasValue ) ;
if ( value . HasValue )
writer . WriteShort ( value . Value ) ;
}
2021-12-27 22:30:22 -08:00
2022-01-13 18:40:24 -08:00
public static void WriteUShort ( this NetworkWriter writer , ushort value )
{
writer . WriteByte ( ( byte ) value ) ;
writer . WriteByte ( ( byte ) ( value > > 8 ) ) ;
}
2021-12-27 22:30:22 -08:00
2022-01-13 18:40:24 -08:00
public static void WriteUShortNullable ( this NetworkWriter writer , ushort? value )
{
writer . WriteBool ( value . HasValue ) ;
if ( value . HasValue )
writer . WriteUShort ( value . Value ) ;
}
2021-12-27 22:30:22 -08:00
2022-01-13 18:40:24 -08:00
public static void WriteInt ( this NetworkWriter writer , int value ) = > writer . WriteUInt ( ( uint ) value ) ;
2021-12-27 22:30:22 -08:00
2022-01-13 18:40:24 -08:00
public static void WriteIntNullable ( this NetworkWriter writer , int? value )
{
writer . WriteBool ( value . HasValue ) ;
if ( value . HasValue )
writer . WriteInt ( value . Value ) ;
}
2021-12-27 22:30:22 -08:00
2022-01-13 18:40:24 -08:00
public static void WriteUInt ( this NetworkWriter writer , uint value )
{
writer . WriteByte ( ( byte ) value ) ;
writer . WriteByte ( ( byte ) ( value > > 8 ) ) ;
writer . WriteByte ( ( byte ) ( value > > 16 ) ) ;
writer . WriteByte ( ( byte ) ( value > > 24 ) ) ;
}
public static void WriteUIntNullable ( this NetworkWriter writer , uint? value )
{
writer . WriteBool ( value . HasValue ) ;
if ( value . HasValue )
writer . WriteUInt ( value . Value ) ;
}
public static void WriteLong ( this NetworkWriter writer , long value ) = > writer . WriteULong ( ( ulong ) value ) ;
public static void WriteLongNullable ( this NetworkWriter writer , long? value )
{
writer . WriteBool ( value . HasValue ) ;
if ( value . HasValue )
writer . WriteLong ( value . Value ) ;
}
public static void WriteULong ( this NetworkWriter writer , ulong value )
{
writer . WriteByte ( ( byte ) value ) ;
writer . WriteByte ( ( byte ) ( value > > 8 ) ) ;
writer . WriteByte ( ( byte ) ( value > > 16 ) ) ;
writer . WriteByte ( ( byte ) ( value > > 24 ) ) ;
writer . WriteByte ( ( byte ) ( value > > 32 ) ) ;
writer . WriteByte ( ( byte ) ( value > > 40 ) ) ;
writer . WriteByte ( ( byte ) ( value > > 48 ) ) ;
writer . WriteByte ( ( byte ) ( value > > 56 ) ) ;
}
public static void WriteULongNullable ( this NetworkWriter writer , ulong? value )
{
writer . WriteBool ( value . HasValue ) ;
if ( value . HasValue )
writer . WriteULong ( value . Value ) ;
}
public static void WriteFloat ( this NetworkWriter writer , float value )
{
UIntFloat converter = new UIntFloat
{
floatValue = value
} ;
writer . WriteUInt ( converter . intValue ) ;
}
public static void WriteFloatNullable ( this NetworkWriter writer , float? value )
{
writer . WriteBool ( value . HasValue ) ;
if ( value . HasValue )
writer . WriteFloat ( value . Value ) ;
}
public static void WriteDouble ( this NetworkWriter writer , double value )
{
UIntDouble converter = new UIntDouble
{
doubleValue = value
} ;
writer . WriteULong ( converter . longValue ) ;
}
public static void WriteDoubleNullable ( this NetworkWriter writer , double? value )
{
writer . WriteBool ( value . HasValue ) ;
if ( value . HasValue )
writer . WriteDouble ( value . Value ) ;
}
public static void WriteDecimal ( this NetworkWriter writer , decimal value )
{
// the only way to read it without allocations is to both read and
// write it with the FloatConverter (which is not binary compatible
// to writer.Write(decimal), hence why we use it here too)
UIntDecimal converter = new UIntDecimal
{
decimalValue = value
} ;
writer . WriteULong ( converter . longValue1 ) ;
writer . WriteULong ( converter . longValue2 ) ;
}
public static void WriteDecimalNullable ( this NetworkWriter writer , decimal? value )
{
writer . WriteBool ( value . HasValue ) ;
if ( value . HasValue )
writer . WriteDecimal ( value . Value ) ;
}
2021-12-27 22:30:22 -08:00
public static void WriteString ( this NetworkWriter writer , string value )
{
// write 0 for null support, increment real size by 1
// (note: original HLAPI would write "" for null strings, but if a
// string is null on the server then it should also be null
// on the client)
if ( value = = null )
{
writer . WriteUShort ( 0 ) ;
return ;
}
// write string with same method as NetworkReader
// convert to byte[]
int size = encoding . GetBytes ( value , 0 , value . Length , stringBuffer , 0 ) ;
// check if within max size
if ( size > = NetworkWriter . MaxStringLength )
{
throw new IndexOutOfRangeException ( $"NetworkWriter.Write(string) too long: {size}. Limit: {NetworkWriter.MaxStringLength}" ) ;
}
// write size and bytes
writer . WriteUShort ( checked ( ( ushort ) ( size + 1 ) ) ) ;
writer . WriteBytes ( stringBuffer , 0 , size ) ;
}
public static void WriteBytesAndSizeSegment ( this NetworkWriter writer , ArraySegment < byte > buffer )
{
writer . WriteBytesAndSize ( buffer . Array , buffer . Offset , buffer . Count ) ;
}
// Weaver needs a write function with just one byte[] parameter
// (we don't name it .Write(byte[]) because it's really a WriteBytesAndSize since we write size / null info too)
public static void WriteBytesAndSize ( this NetworkWriter writer , byte [ ] buffer )
{
// buffer might be null, so we can't use .Length in that case
writer . WriteBytesAndSize ( buffer , 0 , buffer ! = null ? buffer . Length : 0 ) ;
}
// for byte arrays with dynamic size, where the reader doesn't know how many will come
// (like an inventory with different items etc.)
public static void WriteBytesAndSize ( this NetworkWriter writer , byte [ ] buffer , int offset , int count )
{
// null is supported because [SyncVar]s might be structs with null byte[] arrays
// write 0 for null array, increment normal size by 1 to save bandwidth
// (using size=-1 for null would limit max size to 32kb instead of 64kb)
if ( buffer = = null )
{
writer . WriteUInt ( 0 u ) ;
return ;
}
writer . WriteUInt ( checked ( ( uint ) count ) + 1 u ) ;
writer . WriteBytes ( buffer , offset , count ) ;
}
public static void WriteArraySegment < T > ( this NetworkWriter writer , ArraySegment < T > segment )
{
int length = segment . Count ;
writer . WriteInt ( length ) ;
for ( int i = 0 ; i < length ; i + + )
{
writer . Write ( segment . Array [ segment . Offset + i ] ) ;
}
}
2022-01-13 18:40:24 -08:00
public static void WriteVector2 ( this NetworkWriter writer , Vector2 value )
{
writer . WriteFloat ( value . x ) ;
writer . WriteFloat ( value . y ) ;
}
public static void WriteVector2Nullable ( this NetworkWriter writer , Vector2 ? value )
{
writer . WriteBool ( value . HasValue ) ;
if ( value . HasValue )
writer . WriteVector2 ( value . Value ) ;
}
public static void WriteVector3 ( this NetworkWriter writer , Vector3 value )
{
writer . WriteFloat ( value . x ) ;
writer . WriteFloat ( value . y ) ;
writer . WriteFloat ( value . z ) ;
}
2021-12-27 22:30:22 -08:00
2022-01-13 18:40:24 -08:00
public static void WriteVector3Nullable ( this NetworkWriter writer , Vector3 ? value )
{
writer . WriteBool ( value . HasValue ) ;
if ( value . HasValue )
writer . WriteVector3 ( value . Value ) ;
}
2021-12-27 22:30:22 -08:00
2022-01-13 18:40:24 -08:00
public static void WriteVector4 ( this NetworkWriter writer , Vector4 value )
{
writer . WriteFloat ( value . x ) ;
writer . WriteFloat ( value . y ) ;
writer . WriteFloat ( value . z ) ;
writer . WriteFloat ( value . w ) ;
}
2021-12-27 22:30:22 -08:00
2022-01-13 18:40:24 -08:00
public static void WriteVector4Nullable ( this NetworkWriter writer , Vector4 ? value )
{
writer . WriteBool ( value . HasValue ) ;
if ( value . HasValue )
writer . WriteVector4 ( value . Value ) ;
}
2021-12-27 22:30:22 -08:00
2022-01-13 18:40:24 -08:00
public static void WriteVector2Int ( this NetworkWriter writer , Vector2Int value )
{
writer . WriteInt ( value . x ) ;
writer . WriteInt ( value . y ) ;
}
2021-12-27 22:30:22 -08:00
2022-01-13 18:40:24 -08:00
public static void WriteVector2IntNullable ( this NetworkWriter writer , Vector2Int ? value )
{
writer . WriteBool ( value . HasValue ) ;
if ( value . HasValue )
writer . WriteVector2Int ( value . Value ) ;
}
2021-12-27 22:30:22 -08:00
2022-01-13 18:40:24 -08:00
public static void WriteVector3Int ( this NetworkWriter writer , Vector3Int value )
{
writer . WriteInt ( value . x ) ;
writer . WriteInt ( value . y ) ;
writer . WriteInt ( value . z ) ;
}
2021-12-27 22:30:22 -08:00
2022-01-13 18:40:24 -08:00
public static void WriteVector3IntNullable ( this NetworkWriter writer , Vector3Int ? value )
{
writer . WriteBool ( value . HasValue ) ;
if ( value . HasValue )
writer . WriteVector3Int ( value . Value ) ;
}
2021-12-27 22:30:22 -08:00
2022-01-13 18:40:24 -08:00
public static void WriteColor ( this NetworkWriter writer , Color value )
{
writer . WriteFloat ( value . r ) ;
writer . WriteFloat ( value . g ) ;
writer . WriteFloat ( value . b ) ;
writer . WriteFloat ( value . a ) ;
}
2021-12-27 22:30:22 -08:00
2022-01-13 18:40:24 -08:00
public static void WriteColorNullable ( this NetworkWriter writer , Color ? value )
{
writer . WriteBool ( value . HasValue ) ;
if ( value . HasValue )
writer . WriteColor ( value . Value ) ;
}
2021-12-27 22:30:22 -08:00
2022-01-13 18:40:24 -08:00
public static void WriteColor32 ( this NetworkWriter writer , Color32 value )
{
writer . WriteByte ( value . r ) ;
writer . WriteByte ( value . g ) ;
writer . WriteByte ( value . b ) ;
writer . WriteByte ( value . a ) ;
}
2021-12-27 22:30:22 -08:00
2022-01-13 18:40:24 -08:00
public static void WriteColor32Nullable ( this NetworkWriter writer , Color32 ? value )
{
writer . WriteBool ( value . HasValue ) ;
if ( value . HasValue )
writer . WriteColor32 ( value . Value ) ;
}
public static void WriteQuaternion ( this NetworkWriter writer , Quaternion value )
{
writer . WriteFloat ( value . x ) ;
writer . WriteFloat ( value . y ) ;
writer . WriteFloat ( value . z ) ;
writer . WriteFloat ( value . w ) ;
}
public static void WriteQuaternionNullable ( this NetworkWriter writer , Quaternion ? value )
{
writer . WriteBool ( value . HasValue ) ;
if ( value . HasValue )
writer . WriteQuaternion ( value . Value ) ;
}
public static void WriteRect ( this NetworkWriter writer , Rect value )
{
writer . WriteFloat ( value . xMin ) ;
writer . WriteFloat ( value . yMin ) ;
writer . WriteFloat ( value . width ) ;
writer . WriteFloat ( value . height ) ;
}
public static void WriteRectNullable ( this NetworkWriter writer , Rect ? value )
{
writer . WriteBool ( value . HasValue ) ;
if ( value . HasValue )
writer . WriteRect ( value . Value ) ;
}
public static void WritePlane ( this NetworkWriter writer , Plane value )
{
writer . WriteVector3 ( value . normal ) ;
writer . WriteFloat ( value . distance ) ;
}
public static void WritePlaneNullable ( this NetworkWriter writer , Plane ? value )
{
writer . WriteBool ( value . HasValue ) ;
if ( value . HasValue )
writer . WritePlane ( value . Value ) ;
}
public static void WriteRay ( this NetworkWriter writer , Ray value )
{
writer . WriteVector3 ( value . origin ) ;
writer . WriteVector3 ( value . direction ) ;
}
public static void WriteRayNullable ( this NetworkWriter writer , Ray ? value )
{
writer . WriteBool ( value . HasValue ) ;
if ( value . HasValue )
writer . WriteRay ( value . Value ) ;
}
public static void WriteMatrix4x4 ( this NetworkWriter writer , Matrix4x4 value )
{
writer . WriteFloat ( value . m00 ) ;
writer . WriteFloat ( value . m01 ) ;
writer . WriteFloat ( value . m02 ) ;
writer . WriteFloat ( value . m03 ) ;
writer . WriteFloat ( value . m10 ) ;
writer . WriteFloat ( value . m11 ) ;
writer . WriteFloat ( value . m12 ) ;
writer . WriteFloat ( value . m13 ) ;
writer . WriteFloat ( value . m20 ) ;
writer . WriteFloat ( value . m21 ) ;
writer . WriteFloat ( value . m22 ) ;
writer . WriteFloat ( value . m23 ) ;
writer . WriteFloat ( value . m30 ) ;
writer . WriteFloat ( value . m31 ) ;
writer . WriteFloat ( value . m32 ) ;
writer . WriteFloat ( value . m33 ) ;
}
public static void WriteMatrix4x4Nullable ( this NetworkWriter writer , Matrix4x4 ? value )
{
writer . WriteBool ( value . HasValue ) ;
if ( value . HasValue )
writer . WriteMatrix4x4 ( value . Value ) ;
}
2021-12-27 22:30:22 -08:00
public static void WriteGuid ( this NetworkWriter writer , Guid value )
{
byte [ ] data = value . ToByteArray ( ) ;
writer . WriteBytes ( data , 0 , data . Length ) ;
}
public static void WriteGuidNullable ( this NetworkWriter writer , Guid ? value )
{
writer . WriteBool ( value . HasValue ) ;
if ( value . HasValue )
writer . WriteGuid ( value . Value ) ;
}
public static void WriteNetworkIdentity ( this NetworkWriter writer , NetworkIdentity value )
{
if ( value = = null )
{
writer . WriteUInt ( 0 ) ;
return ;
}
writer . WriteUInt ( value . netId ) ;
}
public static void WriteNetworkBehaviour ( this NetworkWriter writer , NetworkBehaviour value )
{
if ( value = = null )
{
writer . WriteUInt ( 0 ) ;
return ;
}
writer . WriteUInt ( value . netId ) ;
writer . WriteByte ( ( byte ) value . ComponentIndex ) ;
}
public static void WriteTransform ( this NetworkWriter writer , Transform value )
{
if ( value = = null )
{
writer . WriteUInt ( 0 ) ;
return ;
}
NetworkIdentity identity = value . GetComponent < NetworkIdentity > ( ) ;
if ( identity ! = null )
{
writer . WriteUInt ( identity . netId ) ;
}
else
{
Debug . LogWarning ( $"NetworkWriter {value} has no NetworkIdentity" ) ;
writer . WriteUInt ( 0 ) ;
}
}
public static void WriteGameObject ( this NetworkWriter writer , GameObject value )
{
if ( value = = null )
{
writer . WriteUInt ( 0 ) ;
return ;
}
NetworkIdentity identity = value . GetComponent < NetworkIdentity > ( ) ;
if ( identity ! = null )
{
writer . WriteUInt ( identity . netId ) ;
}
else
{
Debug . LogWarning ( $"NetworkWriter {value} has no NetworkIdentity" ) ;
writer . WriteUInt ( 0 ) ;
}
}
public static void WriteList < T > ( this NetworkWriter writer , List < T > list )
{
if ( list is null )
{
writer . WriteInt ( - 1 ) ;
return ;
}
writer . WriteInt ( list . Count ) ;
for ( int i = 0 ; i < list . Count ; i + + )
writer . Write ( list [ i ] ) ;
}
public static void WriteArray < T > ( this NetworkWriter writer , T [ ] array )
{
if ( array is null )
{
writer . WriteInt ( - 1 ) ;
return ;
}
writer . WriteInt ( array . Length ) ;
for ( int i = 0 ; i < array . Length ; i + + )
writer . Write ( array [ i ] ) ;
}
public static void WriteUri ( this NetworkWriter writer , Uri uri )
{
writer . WriteString ( uri ? . ToString ( ) ) ;
}
public static void WriteTexture2D ( this NetworkWriter writer , Texture2D texture2D )
{
writer . Write ( texture2D . GetPixels32 ( ) ) ;
}
public static void WriteSprite ( this NetworkWriter writer , Sprite sprite )
{
writer . WriteTexture2D ( sprite . texture ) ;
writer . WriteRect ( sprite . rect ) ;
writer . WriteVector2 ( sprite . pivot ) ;
}
}
}