Mirror/Unity-Technologies-networking/Runtime/NetworkWriter.cs

419 lines
14 KiB
C#
Raw Normal View History

#if ENABLE_UNET
using System;
using System.IO;
namespace UnityEngine.Networking
{
// Binary stream Writer. Supports simple types, buffers, arrays, structs, and nested types
public class NetworkWriter
{
// 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());
// '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()
{
byte[] slice = new byte[Position];
Array.Copy(((MemoryStream)writer.BaseStream).ToArray(), slice, Position);
return slice;
}
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)
{
// 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);
}
// 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)
{
// no null check because we would need to write size info for that too (hence WriteBytesAndSize)
writer.Write(buffer, offset, count);
}
// 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)
{
// 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);
}
// 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)
{
// buffer might be null, so we can't use .Length in that case
WriteBytesAndSize(buffer, 0, buffer != null ? buffer.Length : 0);
}
// 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()
{
writer.BaseStream.Position = 0;
}
public void StartMessage(short msgType)
{
SeekZero();
// two bytes for size, will be filled out in FinishMessage
writer.Write((UInt16)0);
// two bytes for message type
Write(msgType);
}
public void FinishMessage()
{
// 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;
}
// 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;
}
};
}
#endif //ENABLE_UNET