NetworkReader/Writer use C#'s built in BinaryReader/Writer; removed NetworkBuffer; removed redundant .AsArray()/AsArraySegment() functions; removed unnecessary constructors

This commit is contained in:
vis2k 2018-06-14 10:49:13 +02:00
parent c81c670c95
commit 0a34efbaf4
16 changed files with 175 additions and 655 deletions

View File

@ -50,7 +50,6 @@
<Compile Include="..\Runtime\LogFilter.cs" />
<Compile Include="..\Runtime\Messages.cs" />
<Compile Include="..\Runtime\NetworkAnimator.cs" />
<Compile Include="..\Runtime\NetworkBuffer.cs" />
<Compile Include="..\Runtime\NetworkCRC.cs" />
<Compile Include="..\Runtime\NetworkDiscovery.cs" />
<Compile Include="..\Runtime\NetworkHash128.cs" />

View File

@ -1,5 +1,6 @@
#if ENABLE_UNET
using System;
using System.IO;
using System.Collections.Generic;
namespace UnityEngine.Networking
@ -161,7 +162,8 @@ public void CheckInternalBuffer()
public bool SendWriter(NetworkWriter writer)
{
return SendBytes(writer.AsArraySegment().Array, writer.AsArraySegment().Count);
// write relevant data, which is until .Position
return SendBytes(writer.ToArray(), writer.Position);
}
public bool Send(short msgType, MessageBase msg)
@ -176,7 +178,7 @@ public bool Send(short msgType, MessageBase msg)
return SendWriter(writer);
}
internal NetBuffer fragmentBuffer = new NetBuffer();
internal MemoryStream fragmentBuffer = new MemoryStream();
bool readingFragment = false;
internal bool HandleFragment(NetworkReader reader)
@ -186,12 +188,12 @@ internal bool HandleFragment(NetworkReader reader)
{
if (readingFragment == false)
{
fragmentBuffer.SeekZero();
fragmentBuffer.Position = 0;
readingFragment = true;
}
byte[] data = reader.ReadBytesAndSize();
fragmentBuffer.WriteBytes(data, (ushort)data.Length);
fragmentBuffer.Write(data, 0, data.Length);
return false;
}
else
@ -215,7 +217,7 @@ internal bool SendFragmentBytes(byte[] bytes, int bytesToSend)
NetworkWriter fragmentWriter = new NetworkWriter();
fragmentWriter.StartMessage(MsgType.Fragment);
fragmentWriter.Write((byte)0);
fragmentWriter.WriteBytesFull(buffer);
fragmentWriter.WriteBytesAndSize(buffer);
fragmentWriter.FinishMessage();
SendWriter(fragmentWriter);

View File

@ -192,7 +192,6 @@ public static bool AddPlayer(NetworkConnection readyConn, short playerController
var writer = new NetworkWriter();
extraMessage.Serialize(writer);
msg.msgData = writer.ToArray();
msg.msgSize = writer.Position;
}
s_ReadyConnection.Send(MsgType.AddPlayer, msg);
#if ENABLE_UNET_HOST_MIGRATION
@ -241,7 +240,6 @@ public static bool SendReconnectMessage(MessageBase extraMessage)
var writer = new NetworkWriter();
extraMessage.Serialize(writer);
msg.msgData = writer.ToArray();
msg.msgSize = writer.Position;
}
s_ReadyConnection.Send(MsgType.ReconnectPlayer, msg);

View File

@ -82,7 +82,7 @@ private void PostInternalMessage(short msgType)
writer.StartMessage(msgType);
writer.FinishMessage();
PostInternalMessage(writer.AsArray(), 0);
PostInternalMessage(writer.ToArray(), 0);
}
private void ProcessInternalMessages()
@ -129,7 +129,7 @@ internal void InvokeHandlerOnClient(short msgType, MessageBase msg, int channelI
msg.Serialize(writer);
writer.FinishMessage();
InvokeBytesOnClient(writer.AsArray(), channelId);
InvokeBytesOnClient(writer.ToArray(), channelId);
}
// called by the server, to bypass network

View File

@ -45,7 +45,7 @@ public override bool SendBytes(byte[] bytes, int numBytes, int channelId)
public override bool SendWriter(NetworkWriter writer, int channelId)
{
m_LocalClient.InvokeBytesOnClient(writer.AsArray(), channelId);
m_LocalClient.InvokeBytesOnClient(writer.ToArray(), channelId);
return true;
}
@ -105,7 +105,8 @@ public override bool SendBytes(byte[] bytes, int numBytes, int channelId)
public override bool SendWriter(NetworkWriter writer, int channelId)
{
return m_LocalServer.InvokeBytes(this, writer.AsArray(), (short)writer.AsArray().Length, channelId);
// write relevant data, which is until .Position
return m_LocalServer.InvokeBytes(this, writer.ToArray(), (short)writer.Position, channelId);
}
public override void GetStatsOut(out int numMsgs, out int numBufferedMsgs, out int numBytes, out int lastBufferedPerSecond)

View File

@ -108,27 +108,18 @@ public class NotReadyMessage : EmptyMessage
public class AddPlayerMessage : MessageBase
{
public short playerControllerId;
public int msgSize;
public byte[] msgData;
public override void Deserialize(NetworkReader reader)
{
playerControllerId = reader.ReadInt16();
msgData = reader.ReadBytesAndSize();
if (msgData == null)
{
msgSize = 0;
}
else
{
msgSize = msgData.Length;
}
}
public override void Serialize(NetworkWriter writer)
{
writer.Write(playerControllerId);
writer.WriteBytesAndSize(msgData, msgSize);
writer.WriteBytesAndSize(msgData);
}
}
@ -267,7 +258,6 @@ public class ReconnectMessage : MessageBase
public int oldConnectionId;
public short playerControllerId;
public NetworkInstanceId netId;
public int msgSize;
public byte[] msgData;
public override void Deserialize(NetworkReader reader)
@ -276,7 +266,6 @@ public override void Deserialize(NetworkReader reader)
playerControllerId = (short)reader.ReadPackedUInt32();
netId = reader.ReadNetworkId();
msgData = reader.ReadBytesAndSize();
msgSize = msgData.Length;
}
public override void Serialize(NetworkWriter writer)
@ -284,7 +273,7 @@ public override void Serialize(NetworkWriter writer)
writer.WritePackedUInt32((uint)oldConnectionId);
writer.WritePackedUInt32((uint)playerControllerId);
writer.Write(netId);
writer.WriteBytesAndSize(msgData, msgSize);
writer.WriteBytesAndSize(msgData);
}
}
#endif
@ -347,7 +336,7 @@ public override void Serialize(NetworkWriter writer)
writer.Write(assetId);
writer.Write(position);
writer.Write(rotation);
writer.WriteBytesFull(payload);
writer.WriteBytesAndSize(payload);
}
}
@ -371,7 +360,7 @@ public override void Serialize(NetworkWriter writer)
writer.Write(netId);
writer.Write(sceneId);
writer.Write(position);
writer.WriteBytesFull(payload);
writer.WriteBytesAndSize(payload);
}
}
@ -459,7 +448,7 @@ public override void Deserialize(NetworkReader reader)
public override void Serialize(NetworkWriter writer)
{
writer.Write(netId);
writer.WriteBytesFull(payload);
writer.WriteBytesAndSize(payload);
writer.Write(teleport);
writer.WritePackedUInt32((uint)time);
}
@ -485,7 +474,7 @@ public override void Serialize(NetworkWriter writer)
writer.Write(netId);
writer.WritePackedUInt32((uint)stateHash);
writer.Write(normalizedTime);
writer.WriteBytesAndSize(parameters, parameters != null ? parameters.Length : 0);
writer.WriteBytesAndSize(parameters);
}
}
@ -503,7 +492,7 @@ public override void Deserialize(NetworkReader reader)
public override void Serialize(NetworkWriter writer)
{
writer.Write(netId);
writer.WriteBytesAndSize(parameters, parameters != null ? parameters.Length : 0);
writer.WriteBytesAndSize(parameters);
}
}

View File

@ -1,193 +0,0 @@
#if ENABLE_UNET
using System;
using System.Runtime.InteropServices;
namespace UnityEngine.Networking
{
// A growable buffer class used by NetworkReader and NetworkWriter.
// this is used instead of MemoryStream and BinaryReader/BinaryWriter to avoid allocations.
class NetBuffer
{
byte[] m_Buffer;
uint m_Pos;
const int k_InitialSize = 64;
const float k_GrowthFactor = 1.5f;
const int k_BufferSizeWarning = 1024 * 1024 * 128;
public uint Position { get { return m_Pos; } }
public int Length { get { return m_Buffer.Length; } }
public NetBuffer()
{
m_Buffer = new byte[k_InitialSize];
}
// this does NOT copy the buffer
public NetBuffer(byte[] buffer)
{
m_Buffer = buffer;
}
public byte ReadByte()
{
if (m_Pos >= m_Buffer.Length)
{
throw new IndexOutOfRangeException("NetworkReader:ReadByte out of range:" + ToString());
}
return m_Buffer[m_Pos++];
}
public void ReadBytes(byte[] buffer, uint count)
{
if (m_Pos + count > m_Buffer.Length)
{
throw new IndexOutOfRangeException("NetworkReader:ReadBytes out of range: (" + count + ") " + ToString());
}
for (ushort i = 0; i < count; i++)
{
buffer[i] = m_Buffer[m_Pos + i];
}
m_Pos += count;
}
internal ArraySegment<byte> AsArraySegment()
{
return new ArraySegment<byte>(m_Buffer, 0, (int)m_Pos);
}
public void WriteByte(byte value)
{
WriteCheckForSpace(1);
m_Buffer[m_Pos] = value;
m_Pos += 1;
}
public void WriteByte2(byte value0, byte value1)
{
WriteCheckForSpace(2);
m_Buffer[m_Pos] = value0;
m_Buffer[m_Pos + 1] = value1;
m_Pos += 2;
}
public void WriteByte4(byte value0, byte value1, byte value2, byte value3)
{
WriteCheckForSpace(4);
m_Buffer[m_Pos] = value0;
m_Buffer[m_Pos + 1] = value1;
m_Buffer[m_Pos + 2] = value2;
m_Buffer[m_Pos + 3] = value3;
m_Pos += 4;
}
public void WriteByte8(byte value0, byte value1, byte value2, byte value3, byte value4, byte value5, byte value6, byte value7)
{
WriteCheckForSpace(8);
m_Buffer[m_Pos] = value0;
m_Buffer[m_Pos + 1] = value1;
m_Buffer[m_Pos + 2] = value2;
m_Buffer[m_Pos + 3] = value3;
m_Buffer[m_Pos + 4] = value4;
m_Buffer[m_Pos + 5] = value5;
m_Buffer[m_Pos + 6] = value6;
m_Buffer[m_Pos + 7] = value7;
m_Pos += 8;
}
// every other Write() function in this class writes implicitly at the end-marker m_Pos.
// this is the only Write() function that writes to a specific location within the buffer
public void WriteBytesAtOffset(byte[] buffer, ushort targetOffset, ushort count)
{
uint newEnd = (uint)(count + targetOffset);
WriteCheckForSpace((ushort)newEnd);
if (targetOffset == 0 && count == buffer.Length)
{
buffer.CopyTo(m_Buffer, (int)m_Pos);
}
else
{
//CopyTo doesnt take a count :(
for (int i = 0; i < count; i++)
{
m_Buffer[targetOffset + i] = buffer[i];
}
}
// although this writes within the buffer, it could move the end-marker
if (newEnd > m_Pos)
{
m_Pos = newEnd;
}
}
public void WriteBytes(byte[] buffer, ushort count)
{
WriteCheckForSpace(count);
if (count == buffer.Length)
{
buffer.CopyTo(m_Buffer, (int)m_Pos);
}
else
{
//CopyTo doesnt take a count :(
for (int i = 0; i < count; i++)
{
m_Buffer[m_Pos + i] = buffer[i];
}
}
m_Pos += count;
}
void WriteCheckForSpace(ushort count)
{
if (m_Pos + count < m_Buffer.Length)
return;
int newLen = (int)Math.Ceiling(m_Buffer.Length * k_GrowthFactor);
while (m_Pos + count >= newLen)
{
newLen = (int)Math.Ceiling(newLen * k_GrowthFactor);
if (newLen > k_BufferSizeWarning)
{
Debug.LogWarning("NetworkBuffer size is " + newLen + " bytes!");
}
}
// only do the copy once, even if newLen is increased multiple times
byte[] tmp = new byte[newLen];
m_Buffer.CopyTo(tmp, 0);
m_Buffer = tmp;
}
public void FinishMessage()
{
// two shorts (size and msgType) are in header.
ushort sz = (ushort)(m_Pos - (sizeof(ushort) * 2));
m_Buffer[0] = (byte)(sz & 0xff);
m_Buffer[1] = (byte)((sz >> 8) & 0xff);
}
public void SeekZero()
{
m_Pos = 0;
}
public void Replace(byte[] buffer)
{
m_Buffer = buffer;
m_Pos = 0;
}
public override string ToString()
{
return String.Format("NetBuf sz:{0} pos:{1}", m_Buffer.Length, m_Pos);
}
} // end NetBuffer
}
#endif

View File

@ -245,7 +245,7 @@ internal void HandleFragment(NetworkReader reader, int channelId)
var channel = m_Channels[channelId];
if (channel.HandleFragment(reader))
{
NetworkReader msgReader = new NetworkReader(channel.fragmentBuffer.AsArraySegment().Array);
NetworkReader msgReader = new NetworkReader(channel.fragmentBuffer.ToArray());
msgReader.ReadInt16(); // size
short msgType = msgReader.ReadInt16();
InvokeHandler(msgType, msgReader, channelId);

View File

@ -439,7 +439,7 @@ internal bool OnSerializeSafely(NetworkBehaviour comp, NetworkWriter writer, boo
if (LogFilter.logDebug) { Debug.Log("OnSerializeSafely written for object=" + comp.name + " component=" + comp.GetType() + " sceneId=" + m_SceneId + " length=" + bytes.Length); }
// serialize length,data into the real writer, untouched by user code
writer.WriteBytesAndSize(bytes, bytes.Length); // length,data
writer.WriteBytesAndSize(bytes);
return result;
}

View File

@ -876,7 +876,7 @@ internal void OnServerAddPlayerMessageInternal(NetworkMessage netMsg)
AddPlayerMessage msg = new AddPlayerMessage();
netMsg.ReadMessage(msg);
if (msg.msgSize != 0)
if (msg.msgData != null && msg.msgData.Length > 0)
{
var reader = new NetworkReader(msg.msgData);
OnServerAddPlayer(netMsg.conn, msg.playerControllerId, reader);

View File

@ -454,7 +454,7 @@ void OnServerReconnectPlayerMessage(NetworkMessage netMsg)
NetworkReader extraDataReader = null;
if (msg.msgSize != 0)
if (msg.msgData != null && msg.msgData.Length > 0)
{
extraDataReader = new NetworkReader(msg.msgData);
}

View File

@ -1,47 +1,66 @@
#if ENABLE_UNET
using System;
using System.Text;
using UnityEngine;
using System.IO;
namespace UnityEngine.Networking
{
public class NetworkReader
{
NetBuffer m_buf;
const int k_MaxStringLength = 1024 * 32;
public NetworkReader()
{
m_buf = new NetBuffer();
}
public NetworkReader(NetworkWriter writer)
{
m_buf = new NetBuffer(writer.AsArray());
}
BinaryReader reader;
public NetworkReader(byte[] buffer)
{
m_buf = new NetBuffer(buffer);
reader = new BinaryReader(new MemoryStream(buffer));
}
public uint Position { get { return m_buf.Position; } }
public int Length { get { return m_buf.Length; } }
// '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)reader.BaseStream.Position; } set { reader.BaseStream.Position = value; } }
public int Length { get { return (int)reader.BaseStream.Length; } }
public void SeekZero()
{
m_buf.SeekZero();
reader.BaseStream.Position = 0;
}
internal void Replace(byte[] buffer)
public byte ReadByte() { return reader.ReadByte(); }
public sbyte ReadSByte() { return (sbyte)reader.ReadByte(); }
public char ReadChar() { return reader.ReadChar(); }
public bool ReadBoolean() { return reader.ReadBoolean(); }
public short ReadInt16() { return reader.ReadInt16(); }
public ushort ReadUInt16() { return reader.ReadUInt16(); }
public int ReadInt32() { return reader.ReadInt32(); }
public uint ReadUInt32() { return reader.ReadUInt32(); }
public long ReadInt64() { return reader.ReadInt64(); }
public ulong ReadUInt64() { return reader.ReadUInt64(); }
public decimal ReadDecimal() { return reader.ReadDecimal(); }
public float ReadSingle() { return reader.ReadSingle(); }
public double ReadDouble() { return reader.ReadDouble(); }
public string ReadString()
{
m_buf.Replace(buffer);
return reader.ReadBoolean() ? reader.ReadString() : null; // null support, see NetworkWriter
}
public byte[] ReadBytes(int count)
{
return reader.ReadBytes(count);
}
public byte[] ReadBytesAndSize()
{
// notNull? (see NetworkWriter)
bool notNull = reader.ReadBoolean();
if (notNull)
{
ushort size = ReadUInt16();
return reader.ReadBytes(size);
}
return null;
}
// http://sqlite.org/src4/doc/trunk/www/varint.wiki
// NOTE: big endian.
public UInt32 ReadPackedUInt32()
{
byte a0 = ReadByte();
@ -79,54 +98,55 @@ public UInt64 ReadPackedUInt64()
{
return a0;
}
byte a1 = ReadByte();
if (a0 >= 241 && a0 <= 248)
{
return 240 + 256 * (a0 - ((UInt64)241)) + a1;
}
byte a2 = ReadByte();
if (a0 == 249)
{
return 2288 + (((UInt64)256) * a1) + a2;
}
byte a3 = ReadByte();
if (a0 == 250)
{
return a1 + (((UInt64)a2) << 8) + (((UInt64)a3) << 16);
}
byte a4 = ReadByte();
if (a0 == 251)
{
return a1 + (((UInt64)a2) << 8) + (((UInt64)a3) << 16) + (((UInt64)a4) << 24);
}
byte a5 = ReadByte();
if (a0 == 252)
{
return a1 + (((UInt64)a2) << 8) + (((UInt64)a3) << 16) + (((UInt64)a4) << 24) + (((UInt64)a5) << 32);
}
byte a6 = ReadByte();
if (a0 == 253)
{
return a1 + (((UInt64)a2) << 8) + (((UInt64)a3) << 16) + (((UInt64)a4) << 24) + (((UInt64)a5) << 32) + (((UInt64)a6) << 40);
}
byte a7 = ReadByte();
if (a0 == 254)
{
return a1 + (((UInt64)a2) << 8) + (((UInt64)a3) << 16) + (((UInt64)a4) << 24) + (((UInt64)a5) << 32) + (((UInt64)a6) << 40) + (((UInt64)a7) << 48);
}
byte a8 = ReadByte();
if (a0 == 255)
{
return a1 + (((UInt64)a2) << 8) + (((UInt64)a3) << 16) + (((UInt64)a4) << 24) + (((UInt64)a5) << 32) + (((UInt64)a6) << 40) + (((UInt64)a7) << 48) + (((UInt64)a8) << 56);
}
throw new IndexOutOfRangeException("ReadPackedUInt64() failure: " + a0);
}
@ -140,186 +160,6 @@ public NetworkSceneId ReadSceneId()
return new NetworkSceneId(ReadPackedUInt32());
}
public byte ReadByte()
{
return m_buf.ReadByte();
}
public sbyte ReadSByte()
{
return (sbyte)m_buf.ReadByte();
}
public short ReadInt16()
{
ushort value = 0;
value |= m_buf.ReadByte();
value |= (ushort)(m_buf.ReadByte() << 8);
return (short)value;
}
public ushort ReadUInt16()
{
ushort value = 0;
value |= m_buf.ReadByte();
value |= (ushort)(m_buf.ReadByte() << 8);
return value;
}
public int ReadInt32()
{
uint value = 0;
value |= m_buf.ReadByte();
value |= (uint)(m_buf.ReadByte() << 8);
value |= (uint)(m_buf.ReadByte() << 16);
value |= (uint)(m_buf.ReadByte() << 24);
return (int)value;
}
public uint ReadUInt32()
{
uint value = 0;
value |= m_buf.ReadByte();
value |= (uint)(m_buf.ReadByte() << 8);
value |= (uint)(m_buf.ReadByte() << 16);
value |= (uint)(m_buf.ReadByte() << 24);
return value;
}
public long ReadInt64()
{
ulong value = 0;
ulong other = m_buf.ReadByte();
value |= other;
other = ((ulong)m_buf.ReadByte()) << 8;
value |= other;
other = ((ulong)m_buf.ReadByte()) << 16;
value |= other;
other = ((ulong)m_buf.ReadByte()) << 24;
value |= other;
other = ((ulong)m_buf.ReadByte()) << 32;
value |= other;
other = ((ulong)m_buf.ReadByte()) << 40;
value |= other;
other = ((ulong)m_buf.ReadByte()) << 48;
value |= other;
other = ((ulong)m_buf.ReadByte()) << 56;
value |= other;
return (long)value;
}
public ulong ReadUInt64()
{
ulong value = 0;
ulong other = m_buf.ReadByte();
value |= other;
other = ((ulong)m_buf.ReadByte()) << 8;
value |= other;
other = ((ulong)m_buf.ReadByte()) << 16;
value |= other;
other = ((ulong)m_buf.ReadByte()) << 24;
value |= other;
other = ((ulong)m_buf.ReadByte()) << 32;
value |= other;
other = ((ulong)m_buf.ReadByte()) << 40;
value |= other;
other = ((ulong)m_buf.ReadByte()) << 48;
value |= other;
other = ((ulong)m_buf.ReadByte()) << 56;
value |= other;
return value;
}
public decimal ReadDecimal()
{
Int32[] bits = new Int32[4];
bits[0] = ReadInt32();
bits[1] = ReadInt32();
bits[2] = ReadInt32();
bits[3] = ReadInt32();
return new decimal(bits);
}
public float ReadSingle()
{
byte[] bytes = ReadBytes(sizeof(float));
return BitConverter.ToSingle(bytes, 0);
}
public double ReadDouble()
{
byte[] bytes = ReadBytes(sizeof(double));
return BitConverter.ToDouble(bytes, 0);
}
public string ReadString()
{
UInt16 numBytes = ReadUInt16();
if (numBytes == 0)
return "";
if (numBytes >= k_MaxStringLength)
{
throw new IndexOutOfRangeException("ReadString() too long: " + numBytes);
}
Encoding encoding = new UTF8Encoding();
byte[] stringReaderBuffer = new byte[numBytes];
m_buf.ReadBytes(stringReaderBuffer, numBytes);
return encoding.GetString(stringReaderBuffer);
}
public char ReadChar()
{
return (char)m_buf.ReadByte();
}
public bool ReadBoolean()
{
int value = m_buf.ReadByte();
return value == 1;
}
public byte[] ReadBytes(int count)
{
if (count < 0)
{
throw new IndexOutOfRangeException("NetworkReader ReadBytes " + count);
}
byte[] value = new byte[count];
m_buf.ReadBytes(value, (uint)count);
return value;
}
public byte[] ReadBytesAndSize()
{
ushort sz = ReadUInt16();
if (sz == 0)
return new byte[0];
return ReadBytes(sz);
}
public Vector2 ReadVector2()
{
return new Vector2(ReadSingle(), ReadSingle());
@ -478,7 +318,7 @@ public NetworkIdentity ReadNetworkIdentity()
public override string ToString()
{
return m_buf.ToString();
return reader.ToString();
}
public TMsg ReadMessage<TMsg>() where TMsg : MessageBase, new()

View File

@ -425,7 +425,8 @@ static public void SendWriterToReady(GameObject contextObj, NetworkWriter writer
{
throw new UnityException("NetworkWriter used buffer is too big!");
}
SendBytesToReady(contextObj, writer.AsArraySegment().Array, writer.AsArraySegment().Count, channelId);
// send relevant data, which is until .Position
SendBytesToReady(contextObj, writer.ToArray(), writer.Position, channelId);
}
static public void SendBytesToReady(GameObject contextObj, byte[] buffer, int numBytes, int channelId)
@ -722,7 +723,7 @@ private void GenerateError(NetworkConnection conn, byte error)
msg.Serialize(writer);
// pass a reader (attached to local buffer) to handler
NetworkReader reader = new NetworkReader(writer);
NetworkReader reader = new NetworkReader(writer.ToArray());
conn.InvokeHandler(MsgType.Error, reader, 0);
}
}
@ -1565,7 +1566,7 @@ internal bool InvokeHandlerOnServer(ULocalConnectionToServer conn, short msgType
msg.Serialize(writer);
// pass a reader (attached to local buffer) to handler
NetworkReader reader = new NetworkReader(writer);
NetworkReader reader = new NetworkReader(writer.ToArray());
// this must be invoked with the connection to the client, not the client's connection to the server
m_LocalConnection.InvokeHandler(msgType, reader, channelId);

View File

@ -1,60 +1,95 @@
#if ENABLE_UNET
using System;
using System.Text;
using UnityEngine;
using System.IO;
namespace UnityEngine.Networking
{
// Binary stream Writer. Supports simple types, buffers, arrays, structs, and nested types
public class NetworkWriter
{
const int k_MaxStringLength = 1024 * 32;
NetBuffer m_Buffer;
public NetworkWriter()
{
m_Buffer = new NetBuffer();
}
public NetworkWriter(byte[] buffer)
{
m_Buffer = new NetBuffer(buffer);
}
// 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
// 'uint' is exact but we serialize/deserialize array.Lengths often which are int's, so 'int' is easier and big enough too.
public int Position
{
get
{
// uint to int conversion check. better safe than sorry.
if (m_Buffer.Position > Int32.MaxValue)
{
if (LogFilter.logError) { Debug.LogError("NetworkWriter.Position exceeds Int32.MaxValue"); }
}
return (int)m_Buffer.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()
{
var newArray = new byte[m_Buffer.AsArraySegment().Count];
Array.Copy(m_Buffer.AsArraySegment().Array, newArray, m_Buffer.AsArraySegment().Count);
return newArray;
byte[] slice = new byte[Position];
Array.Copy(((MemoryStream)writer.BaseStream).ToArray(), slice, Position);
return slice;
}
public byte[] AsArray()
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)
{
return AsArraySegment().Array;
// 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);
}
internal ArraySegment<byte> AsArraySegment()
// 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)
{
return m_Buffer.AsArraySegment();
// 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)
@ -187,183 +222,6 @@ public void Write(NetworkSceneId value)
WritePackedUInt32(value.Value);
}
public void Write(char value)
{
m_Buffer.WriteByte((byte)value);
}
public void Write(byte value)
{
m_Buffer.WriteByte(value);
}
public void Write(sbyte value)
{
m_Buffer.WriteByte((byte)value);
}
public void Write(short value)
{
m_Buffer.WriteByte2((byte)(value & 0xff), (byte)((value >> 8) & 0xff));
}
public void Write(ushort value)
{
m_Buffer.WriteByte2((byte)(value & 0xff), (byte)((value >> 8) & 0xff));
}
public void Write(int value)
{
// little endian...
m_Buffer.WriteByte4(
(byte)(value & 0xff),
(byte)((value >> 8) & 0xff),
(byte)((value >> 16) & 0xff),
(byte)((value >> 24) & 0xff));
}
public void Write(uint value)
{
m_Buffer.WriteByte4(
(byte)(value & 0xff),
(byte)((value >> 8) & 0xff),
(byte)((value >> 16) & 0xff),
(byte)((value >> 24) & 0xff));
}
public void Write(long value)
{
m_Buffer.WriteByte8(
(byte)(value & 0xff),
(byte)((value >> 8) & 0xff),
(byte)((value >> 16) & 0xff),
(byte)((value >> 24) & 0xff),
(byte)((value >> 32) & 0xff),
(byte)((value >> 40) & 0xff),
(byte)((value >> 48) & 0xff),
(byte)((value >> 56) & 0xff));
}
public void Write(ulong value)
{
m_Buffer.WriteByte8(
(byte)(value & 0xff),
(byte)((value >> 8) & 0xff),
(byte)((value >> 16) & 0xff),
(byte)((value >> 24) & 0xff),
(byte)((value >> 32) & 0xff),
(byte)((value >> 40) & 0xff),
(byte)((value >> 48) & 0xff),
(byte)((value >> 56) & 0xff));
}
public void Write(float value)
{
byte[] bytes = BitConverter.GetBytes(value);
Write(bytes, bytes.Length);
}
public void Write(double value)
{
byte[] bytes = BitConverter.GetBytes(value);
Write(bytes, bytes.Length);
}
public void Write(decimal value)
{
Int32[] bits = decimal.GetBits(value);
Write(bits[0]);
Write(bits[1]);
Write(bits[2]);
Write(bits[3]);
}
public void Write(string value)
{
if (value == null)
{
m_Buffer.WriteByte2(0, 0);
return;
}
Encoding encoding = new UTF8Encoding();
byte[] stringWriteBuffer = new byte[k_MaxStringLength];
int len = encoding.GetByteCount(value);
if (len >= k_MaxStringLength)
{
throw new IndexOutOfRangeException("Serialize(string) too long: " + value.Length);
}
Write((ushort)(len));
int numBytes = encoding.GetBytes(value, 0, value.Length, stringWriteBuffer, 0);
m_Buffer.WriteBytes(stringWriteBuffer, (ushort)numBytes);
}
public void Write(bool value)
{
if (value)
m_Buffer.WriteByte(1);
else
m_Buffer.WriteByte(0);
}
public void Write(byte[] buffer, int count)
{
if (count > UInt16.MaxValue)
{
if (LogFilter.logError) { Debug.LogError("NetworkWriter Write: buffer is too large (" + count + ") bytes. The maximum buffer size is 64K bytes."); }
return;
}
m_Buffer.WriteBytes(buffer, (UInt16)count);
}
public void Write(byte[] buffer, int offset, int count)
{
if (count > UInt16.MaxValue)
{
if (LogFilter.logError) { Debug.LogError("NetworkWriter Write: buffer is too large (" + count + ") bytes. The maximum buffer size is 64K bytes."); }
return;
}
m_Buffer.WriteBytesAtOffset(buffer, (ushort)offset, (ushort)count);
}
public void WriteBytesAndSize(byte[] buffer, int count)
{
if (buffer == null || count == 0)
{
Write((UInt16)0);
return;
}
if (count > UInt16.MaxValue)
{
if (LogFilter.logError) { Debug.LogError("NetworkWriter WriteBytesAndSize: buffer is too large (" + count + ") bytes. The maximum buffer size is 64K bytes."); }
return;
}
Write((UInt16)count);
m_Buffer.WriteBytes(buffer, (UInt16)count);
}
//NOTE: this will write the entire buffer.. including trailing empty space!
public void WriteBytesFull(byte[] buffer)
{
if (buffer == null)
{
Write((UInt16)0);
return;
}
if (buffer.Length > UInt16.MaxValue)
{
if (LogFilter.logError) { Debug.LogError("NetworkWriter WriteBytes: buffer is too large (" + buffer.Length + ") bytes. The maximum buffer size is 64K bytes."); }
return;
}
Write((UInt16)buffer.Length);
m_Buffer.WriteBytes(buffer, (UInt16)buffer.Length);
}
public void Write(Vector2 value)
{
Write(value.x);
@ -524,7 +382,7 @@ public void Write(MessageBase msg)
public void SeekZero()
{
m_Buffer.SeekZero();
writer.BaseStream.Position = 0;
}
public void StartMessage(short msgType)
@ -532,7 +390,7 @@ public void StartMessage(short msgType)
SeekZero();
// two bytes for size, will be filled out in FinishMessage
m_Buffer.WriteByte2(0, 0);
writer.Write((UInt16)0);
// two bytes for message type
Write(msgType);
@ -540,8 +398,13 @@ public void StartMessage(short msgType)
public void FinishMessage()
{
// writes correct size into space at start of buffer
m_Buffer.FinishMessage();
// 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;
}
};
}

View File

@ -49,7 +49,6 @@
<Compile Include="LogFilter.cs" />
<Compile Include="Messages.cs" />
<Compile Include="NetworkAnimator.cs" />
<Compile Include="NetworkBuffer.cs" />
<Compile Include="NetworkCRC.cs" />
<Compile Include="NetworkDiscovery.cs" />
<Compile Include="NetworkHash128.cs" />

View File

@ -1191,6 +1191,27 @@ static MethodReference ResolveMethodWithArg(TypeReference t, string name, TypeRe
return null;
}
// System.Byte[] arguments need a version with a string
static MethodReference ResolveMethodWithArg(TypeReference t, string name, string argTypeFullName)
{
foreach (var methodRef in t.Resolve().Methods)
{
if (methodRef.Name == name)
{
if (methodRef.Parameters.Count == 1)
{
if (methodRef.Parameters[0].ParameterType.FullName == argTypeFullName)
{
return scriptDef.MainModule.ImportReference(methodRef);
}
}
}
}
Log.Error("ResolveMethodWithArg failed " + t.Name + "::" + name + " " + argTypeFullName);
fail = true;
return null;
}
static MethodDefinition ResolveDefaultPublicCtor(TypeReference variable)
{
foreach (MethodDefinition methodRef in variable.Resolve().Methods)
@ -1541,7 +1562,7 @@ static void SetupWriteFunctions()
{ NetworkInstanceIdType.FullName, NetworkWriterWriteNetworkInstanceId },
{ NetworkSceneIdType.FullName, NetworkWriterWriteNetworkSceneId },
{ transformType.FullName, ResolveMethodWithArg(NetworkWriterType, "Write", transformType) },
{ "System.Byte[]", ResolveMethod(NetworkWriterType, "WriteBytesFull") },
{ "System.Byte[]", ResolveMethodWithArg(NetworkWriterType, "WriteBytesAndSize", "System.Byte[]") },
{ SyncListFloatType.FullName, SyncListFloatWriteType },
{ SyncListIntType.FullName, SyncListIntWriteType },
{ SyncListUIntType.FullName, SyncListUIntWriteType },