mirror of
https://github.com/MirrorNetworking/Mirror.git
synced 2024-11-18 11:00:32 +00:00
419 lines
14 KiB
C#
419 lines
14 KiB
C#
#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
|