2018-06-07 13:41:08 +00:00
#if ENABLE_UNET
using System ;
2018-06-14 08:49:13 +00:00
using System.IO ;
2018-06-07 13:41:08 +00:00
namespace UnityEngine.Networking
{
// Binary stream Writer. Supports simple types, buffers, arrays, structs, and nested types
public class NetworkWriter
{
2018-06-14 08:49:13 +00:00
// create writer immediately with it's own buffer so no one can mess with it and so that we can resize it.
public BinaryWriter writer = new BinaryWriter ( new MemoryStream ( ) ) ;
2018-06-07 13:41:08 +00:00
2018-06-14 08:49:13 +00:00
// 'int' is the best type for .Position. 'short' is too small if we send >32kb which would result in negative .Position
// -> converting long to int is fine until 2GB of data (MAX_INT), so we don't have to worry about overflows here
public int Position { get { return ( int ) writer . BaseStream . Position ; } set { writer . BaseStream . Position = value ; } }
// MemoryStream.ToArray() ignores .Position, but HLAPI's .ToArray() expects only the valid data until .Position.
// .ToArray() is often used for payloads or sends, we don't unnecessary old data in there (bandwidth etc.)
// Example:
// HLAPI writes 10 bytes, sends them
// HLAPI sets .Position = 0
// HLAPI writes 5 bytes, sends them
// => .ToArray() would return 10 bytes because of the first write, which is exactly what we don't want.
public byte [ ] ToArray ( )
2018-06-07 13:41:08 +00:00
{
2018-06-14 08:49:13 +00:00
byte [ ] slice = new byte [ Position ] ;
Array . Copy ( ( ( MemoryStream ) writer . BaseStream ) . ToArray ( ) , slice , Position ) ;
return slice ;
2018-06-07 13:41:08 +00:00
}
2018-06-14 08:49:13 +00:00
public void Write ( byte value ) { writer . Write ( value ) ; }
public void Write ( sbyte value ) { writer . Write ( value ) ; }
public void Write ( char value ) { writer . Write ( value ) ; }
public void Write ( bool value ) { writer . Write ( value ) ; }
public void Write ( short value ) { writer . Write ( value ) ; }
public void Write ( ushort value ) { writer . Write ( value ) ; }
public void Write ( int value ) { writer . Write ( value ) ; }
public void Write ( uint value ) { writer . Write ( value ) ; }
public void Write ( long value ) { writer . Write ( value ) ; }
public void Write ( ulong value ) { writer . Write ( value ) ; }
public void Write ( float value ) { writer . Write ( value ) ; }
public void Write ( double value ) { writer . Write ( value ) ; }
public void Write ( decimal value ) { writer . Write ( value ) ; }
public void Write ( string value )
2018-06-07 13:41:08 +00:00
{
2018-06-14 08:49:13 +00:00
// BinaryWriter doesn't support null strings, so let's write an extra boolean for that
// (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)
writer . Write ( value ! = null ) ;
if ( value ! = null ) writer . Write ( value ) ;
2018-06-07 13:41:08 +00:00
}
2018-06-14 08:49:13 +00: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 Write ( byte [ ] buffer , int offset , int count )
2018-06-07 13:41:08 +00:00
{
2018-06-14 08:49:13 +00:00
// no null check because we would need to write size info for that too (hence WriteBytesAndSize)
writer . Write ( buffer , offset , count ) ;
2018-06-07 13:41:08 +00:00
}
2018-06-14 08:49:13 +00:00
// for byte arrays with dynamic size, where the reader doesn't know how many will come
// (like an inventory with different items etc.)
public void WriteBytesAndSize ( byte [ ] buffer , int offset , int count )
2018-06-07 13:41:08 +00:00
{
2018-06-14 08:49:13 +00:00
// null is supported because [SyncVar]s might be structs with null byte[] arrays
// (writing a size=0 empty array is not the same, the server and client would be out of sync)
// (using size=-1 for null would limit max size to 32kb instead of 64kb)
if ( buffer = = null )
{
writer . Write ( false ) ; // notNull?
return ;
}
if ( count > UInt16 . MaxValue )
{
if ( LogFilter . logError ) { Debug . LogError ( "NetworkWriter WriteBytesAndSize: size is too large (" + count + ") bytes. The maximum buffer size is " + UInt16 . MaxValue + " bytes." ) ; }
return ;
}
writer . Write ( true ) ; // notNull?
writer . Write ( ( UInt16 ) count ) ;
writer . Write ( buffer , offset , count ) ;
2018-06-07 13:41:08 +00:00
}
2018-06-14 08:49:13 +00:00
// UNETWeaver 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 void WriteBytesAndSize ( byte [ ] buffer )
2018-06-07 13:41:08 +00:00
{
2018-06-14 08:49:13 +00:00
// buffer might be null, so we can't use .Length in that case
WriteBytesAndSize ( buffer , 0 , buffer ! = null ? buffer . Length : 0 ) ;
2018-06-07 13:41:08 +00:00
}
// http://sqlite.org/src4/doc/trunk/www/varint.wiki
public void WritePackedUInt32 ( UInt32 value )
{
if ( value < = 240 )
{
Write ( ( byte ) value ) ;
return ;
}
if ( value < = 2287 )
{
Write ( ( byte ) ( ( value - 240 ) / 256 + 241 ) ) ;
Write ( ( byte ) ( ( value - 240 ) % 256 ) ) ;
return ;
}
if ( value < = 67823 )
{
Write ( ( byte ) 249 ) ;
Write ( ( byte ) ( ( value - 2288 ) / 256 ) ) ;
Write ( ( byte ) ( ( value - 2288 ) % 256 ) ) ;
return ;
}
if ( value < = 16777215 )
{
Write ( ( byte ) 250 ) ;
Write ( ( byte ) ( value & 0xFF ) ) ;
Write ( ( byte ) ( ( value > > 8 ) & 0xFF ) ) ;
Write ( ( byte ) ( ( value > > 16 ) & 0xFF ) ) ;
return ;
}
// all other values of uint
Write ( ( byte ) 251 ) ;
Write ( ( byte ) ( value & 0xFF ) ) ;
Write ( ( byte ) ( ( value > > 8 ) & 0xFF ) ) ;
Write ( ( byte ) ( ( value > > 16 ) & 0xFF ) ) ;
Write ( ( byte ) ( ( value > > 24 ) & 0xFF ) ) ;
}
public void WritePackedUInt64 ( UInt64 value )
{
if ( value < = 240 )
{
Write ( ( byte ) value ) ;
return ;
}
if ( value < = 2287 )
{
Write ( ( byte ) ( ( value - 240 ) / 256 + 241 ) ) ;
Write ( ( byte ) ( ( value - 240 ) % 256 ) ) ;
return ;
}
if ( value < = 67823 )
{
Write ( ( byte ) 249 ) ;
Write ( ( byte ) ( ( value - 2288 ) / 256 ) ) ;
Write ( ( byte ) ( ( value - 2288 ) % 256 ) ) ;
return ;
}
if ( value < = 16777215 )
{
Write ( ( byte ) 250 ) ;
Write ( ( byte ) ( value & 0xFF ) ) ;
Write ( ( byte ) ( ( value > > 8 ) & 0xFF ) ) ;
Write ( ( byte ) ( ( value > > 16 ) & 0xFF ) ) ;
return ;
}
if ( value < = 4294967295 )
{
Write ( ( byte ) 251 ) ;
Write ( ( byte ) ( value & 0xFF ) ) ;
Write ( ( byte ) ( ( value > > 8 ) & 0xFF ) ) ;
Write ( ( byte ) ( ( value > > 16 ) & 0xFF ) ) ;
Write ( ( byte ) ( ( value > > 24 ) & 0xFF ) ) ;
return ;
}
if ( value < = 1099511627775 )
{
Write ( ( byte ) 252 ) ;
Write ( ( byte ) ( value & 0xFF ) ) ;
Write ( ( byte ) ( ( value > > 8 ) & 0xFF ) ) ;
Write ( ( byte ) ( ( value > > 16 ) & 0xFF ) ) ;
Write ( ( byte ) ( ( value > > 24 ) & 0xFF ) ) ;
Write ( ( byte ) ( ( value > > 32 ) & 0xFF ) ) ;
return ;
}
if ( value < = 281474976710655 )
{
Write ( ( byte ) 253 ) ;
Write ( ( byte ) ( value & 0xFF ) ) ;
Write ( ( byte ) ( ( value > > 8 ) & 0xFF ) ) ;
Write ( ( byte ) ( ( value > > 16 ) & 0xFF ) ) ;
Write ( ( byte ) ( ( value > > 24 ) & 0xFF ) ) ;
Write ( ( byte ) ( ( value > > 32 ) & 0xFF ) ) ;
Write ( ( byte ) ( ( value > > 40 ) & 0xFF ) ) ;
return ;
}
if ( value < = 72057594037927935 )
{
Write ( ( byte ) 254 ) ;
Write ( ( byte ) ( value & 0xFF ) ) ;
Write ( ( byte ) ( ( value > > 8 ) & 0xFF ) ) ;
Write ( ( byte ) ( ( value > > 16 ) & 0xFF ) ) ;
Write ( ( byte ) ( ( value > > 24 ) & 0xFF ) ) ;
Write ( ( byte ) ( ( value > > 32 ) & 0xFF ) ) ;
Write ( ( byte ) ( ( value > > 40 ) & 0xFF ) ) ;
Write ( ( byte ) ( ( value > > 48 ) & 0xFF ) ) ;
return ;
}
// all others
{
Write ( ( byte ) 255 ) ;
Write ( ( byte ) ( value & 0xFF ) ) ;
Write ( ( byte ) ( ( value > > 8 ) & 0xFF ) ) ;
Write ( ( byte ) ( ( value > > 16 ) & 0xFF ) ) ;
Write ( ( byte ) ( ( value > > 24 ) & 0xFF ) ) ;
Write ( ( byte ) ( ( value > > 32 ) & 0xFF ) ) ;
Write ( ( byte ) ( ( value > > 40 ) & 0xFF ) ) ;
Write ( ( byte ) ( ( value > > 48 ) & 0xFF ) ) ;
Write ( ( byte ) ( ( value > > 56 ) & 0xFF ) ) ;
}
}
public void Write ( NetworkInstanceId value )
{
WritePackedUInt32 ( value . Value ) ;
}
public void Write ( NetworkSceneId value )
{
WritePackedUInt32 ( value . Value ) ;
}
public void Write ( Vector2 value )
{
Write ( value . x ) ;
Write ( value . y ) ;
}
public void Write ( Vector3 value )
{
Write ( value . x ) ;
Write ( value . y ) ;
Write ( value . z ) ;
}
public void Write ( Vector4 value )
{
Write ( value . x ) ;
Write ( value . y ) ;
Write ( value . z ) ;
Write ( value . w ) ;
}
public void Write ( Color value )
{
Write ( value . r ) ;
Write ( value . g ) ;
Write ( value . b ) ;
Write ( value . a ) ;
}
public void Write ( Color32 value )
{
Write ( value . r ) ;
Write ( value . g ) ;
Write ( value . b ) ;
Write ( value . a ) ;
}
public void Write ( Quaternion value )
{
Write ( value . x ) ;
Write ( value . y ) ;
Write ( value . z ) ;
Write ( value . w ) ;
}
public void Write ( Rect value )
{
Write ( value . xMin ) ;
Write ( value . yMin ) ;
Write ( value . width ) ;
Write ( value . height ) ;
}
public void Write ( Plane value )
{
Write ( value . normal ) ;
Write ( value . distance ) ;
}
public void Write ( Ray value )
{
Write ( value . direction ) ;
Write ( value . origin ) ;
}
public void Write ( Matrix4x4 value )
{
Write ( value . m00 ) ;
Write ( value . m01 ) ;
Write ( value . m02 ) ;
Write ( value . m03 ) ;
Write ( value . m10 ) ;
Write ( value . m11 ) ;
Write ( value . m12 ) ;
Write ( value . m13 ) ;
Write ( value . m20 ) ;
Write ( value . m21 ) ;
Write ( value . m22 ) ;
Write ( value . m23 ) ;
Write ( value . m30 ) ;
Write ( value . m31 ) ;
Write ( value . m32 ) ;
Write ( value . m33 ) ;
}
public void Write ( NetworkHash128 value )
{
Write ( value . i0 ) ;
Write ( value . i1 ) ;
Write ( value . i2 ) ;
Write ( value . i3 ) ;
Write ( value . i4 ) ;
Write ( value . i5 ) ;
Write ( value . i6 ) ;
Write ( value . i7 ) ;
Write ( value . i8 ) ;
Write ( value . i9 ) ;
Write ( value . i10 ) ;
Write ( value . i11 ) ;
Write ( value . i12 ) ;
Write ( value . i13 ) ;
Write ( value . i14 ) ;
Write ( value . i15 ) ;
}
public void Write ( NetworkIdentity value )
{
if ( value = = null )
{
WritePackedUInt32 ( 0 ) ;
return ;
}
Write ( value . netId ) ;
}
public void Write ( Transform value )
{
if ( value = = null | | value . gameObject = = null )
{
WritePackedUInt32 ( 0 ) ;
return ;
}
var uv = value . gameObject . GetComponent < NetworkIdentity > ( ) ;
if ( uv ! = null )
{
Write ( uv . netId ) ;
}
else
{
if ( LogFilter . logWarn ) { Debug . LogWarning ( "NetworkWriter " + value + " has no NetworkIdentity" ) ; }
WritePackedUInt32 ( 0 ) ;
}
}
public void Write ( GameObject value )
{
if ( value = = null )
{
WritePackedUInt32 ( 0 ) ;
return ;
}
var uv = value . GetComponent < NetworkIdentity > ( ) ;
if ( uv ! = null )
{
Write ( uv . netId ) ;
}
else
{
if ( LogFilter . logWarn ) { Debug . LogWarning ( "NetworkWriter " + value + " has no NetworkIdentity" ) ; }
WritePackedUInt32 ( 0 ) ;
}
}
public void Write ( MessageBase msg )
{
msg . Serialize ( this ) ;
}
public void SeekZero ( )
{
2018-06-14 08:49:13 +00:00
writer . BaseStream . Position = 0 ;
2018-06-07 13:41:08 +00:00
}
public void StartMessage ( short msgType )
{
SeekZero ( ) ;
// two bytes for size, will be filled out in FinishMessage
2018-06-14 08:49:13 +00:00
writer . Write ( ( UInt16 ) 0 ) ;
2018-06-07 13:41:08 +00:00
// two bytes for message type
Write ( msgType ) ;
}
public void FinishMessage ( )
{
2018-06-21 21:03:10 +00:00
// size has to fit into ushort
if ( Position > UInt16 . MaxValue )
{
if ( LogFilter . logError ) { Debug . LogError ( "NetworkWriter FinishMessage: size is too large (" + Position + ") bytes. The maximum buffer size is " + UInt16 . MaxValue + " bytes." ) ; }
return ;
}
2018-06-14 08:49:13 +00:00
// jump to zero, replace size (ushort) in header, jump back
long oldPosition = Position ;
ushort size = ( ushort ) ( Position - ( sizeof ( UInt16 ) * 2 ) ) ; // length - two shorts header (size, msgType)
SeekZero ( ) ;
Write ( size ) ;
writer . BaseStream . Position = oldPosition ;
2018-06-07 13:41:08 +00:00
}
} ;
}
#endif //ENABLE_UNET