mirror of
https://github.com/MirrorNetworking/Mirror.git
synced 2024-11-18 02:50:32 +00:00
Revert "perf: NetworkWriter/Reader Write/ReadBlittable<T> for 4-6x performance improvement! (#2441)" (#2526)
This reverts commit 1947f061ad
and applies some patches to bring it back up to date without the DOTSNET feature.
This commit is contained in:
parent
eb6b7e73b9
commit
dd4d344542
@ -8,7 +8,7 @@
|
|||||||
"optionalUnityReferences": [],
|
"optionalUnityReferences": [],
|
||||||
"includePlatforms": [],
|
"includePlatforms": [],
|
||||||
"excludePlatforms": [],
|
"excludePlatforms": [],
|
||||||
"allowUnsafeCode": true,
|
"allowUnsafeCode": false,
|
||||||
"overrideReferences": false,
|
"overrideReferences": false,
|
||||||
"precompiledReferences": [],
|
"precompiledReferences": [],
|
||||||
"autoReferenced": true,
|
"autoReferenced": true,
|
||||||
|
@ -1,8 +1,15 @@
|
|||||||
|
// Custom NetworkReader that doesn't use C#'s built in MemoryStream in order to
|
||||||
|
// avoid allocations.
|
||||||
|
//
|
||||||
|
// Benchmark: 100kb byte[] passed to NetworkReader constructor 1000x
|
||||||
|
// before with MemoryStream
|
||||||
|
// 0.8% CPU time, 250KB memory, 3.82ms
|
||||||
|
// now:
|
||||||
|
// 0.0% CPU time, 32KB memory, 0.02ms
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Unity.Collections.LowLevel.Unsafe;
|
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace Mirror
|
namespace Mirror
|
||||||
@ -68,59 +75,13 @@ public NetworkReader(ArraySegment<byte> segment)
|
|||||||
buffer = segment;
|
buffer = segment;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadBlittable<T> from DOTSNET
|
public byte ReadByte()
|
||||||
// Benchmark: see NetworkWriter.WriteBlittable!
|
|
||||||
/// <summary>
|
|
||||||
/// Read blittable type from buffer
|
|
||||||
/// <para>
|
|
||||||
/// this is extremely fast, but only works for blittable types.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// Note:
|
|
||||||
/// ReadBlittable assumes same endianness for server and client.
|
|
||||||
/// All Unity 2018+ platforms are little endian.
|
|
||||||
/// </para>
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// See <see href="https://docs.microsoft.com/en-us/dotnet/framework/interop/blittable-and-non-blittable-types">Blittable and Non-Blittable Types</see>
|
|
||||||
/// for more info.
|
|
||||||
/// </remarks>
|
|
||||||
/// <typeparam name="T">Needs to be unmanaged, see <see href="https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/unmanaged-types">unmanaged types</see></typeparam>
|
|
||||||
/// <returns></returns>
|
|
||||||
public unsafe T ReadBlittable<T>()
|
|
||||||
where T : unmanaged
|
|
||||||
{
|
{
|
||||||
// check if blittable for safety
|
if (Position + 1 > buffer.Count)
|
||||||
#if UNITY_EDITOR
|
|
||||||
if (!UnsafeUtility.IsBlittable(typeof(T)))
|
|
||||||
{
|
{
|
||||||
throw new ArgumentException(typeof(T) + " is not blittable!");
|
throw new EndOfStreamException("ReadByte out of range:" + ToString());
|
||||||
}
|
}
|
||||||
#endif
|
return buffer.Array[buffer.Offset + Position++];
|
||||||
|
|
||||||
// calculate size
|
|
||||||
// sizeof(T) gets the managed size at compile time.
|
|
||||||
// Marshal.SizeOf<T> gets the unmanaged size at runtime (slow).
|
|
||||||
// => our 1mio writes benchmark is 6x slower with Marshal.SizeOf<T>
|
|
||||||
// => for blittable types, sizeof(T) is even recommended:
|
|
||||||
// https://docs.microsoft.com/en-us/dotnet/standard/native-interop/best-practices
|
|
||||||
int size = sizeof(T);
|
|
||||||
|
|
||||||
// enough data to read?
|
|
||||||
if (Position + size > buffer.Count)
|
|
||||||
{
|
|
||||||
throw new EndOfStreamException($"ReadBlittable<{typeof(T)}> out of range: {ToString()}");
|
|
||||||
}
|
|
||||||
|
|
||||||
// read blittable
|
|
||||||
T value;
|
|
||||||
fixed (byte* ptr = &buffer.Array[buffer.Offset + Position])
|
|
||||||
{
|
|
||||||
// cast buffer to a T* pointer and then read from it.
|
|
||||||
value = *(T*)ptr;
|
|
||||||
}
|
|
||||||
Position += size;
|
|
||||||
return value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -183,12 +144,9 @@ public T Read<T>()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Built in Reader functions for Mirror
|
// Mirror's Weaver automatically detects all NetworkReader function types,
|
||||||
/// <para>
|
// but they do all need to be extensions.
|
||||||
/// Weaver automatically detects all extension methods for NetworkWriter
|
|
||||||
/// </para>
|
|
||||||
/// </summary>
|
|
||||||
public static class NetworkReaderExtensions
|
public static class NetworkReaderExtensions
|
||||||
{
|
{
|
||||||
static readonly ILogger logger = LogFactory.GetLogger(typeof(NetworkReaderExtensions));
|
static readonly ILogger logger = LogFactory.GetLogger(typeof(NetworkReaderExtensions));
|
||||||
@ -198,19 +156,61 @@ public static class NetworkReaderExtensions
|
|||||||
// 1000 readers after: 0.8MB GC, 18ms
|
// 1000 readers after: 0.8MB GC, 18ms
|
||||||
static readonly UTF8Encoding encoding = new UTF8Encoding(false, true);
|
static readonly UTF8Encoding encoding = new UTF8Encoding(false, true);
|
||||||
|
|
||||||
public static byte ReadByte(this NetworkReader reader) => reader.ReadBlittable<byte>();
|
public static byte ReadByte(this NetworkReader reader) => reader.ReadByte();
|
||||||
public static sbyte ReadSByte(this NetworkReader reader) => reader.ReadBlittable<sbyte>();
|
public static sbyte ReadSByte(this NetworkReader reader) => (sbyte)reader.ReadByte();
|
||||||
public static char ReadChar(this NetworkReader reader) => (char)reader.ReadBlittable<short>(); // char isn't blittable
|
public static char ReadChar(this NetworkReader reader) => (char)reader.ReadUInt16();
|
||||||
public static bool ReadBoolean(this NetworkReader reader) => reader.ReadBlittable<byte>() != 0; // bool isn't blittable
|
public static bool ReadBoolean(this NetworkReader reader) => reader.ReadByte() != 0;
|
||||||
public static short ReadInt16(this NetworkReader reader) => reader.ReadBlittable<short>();
|
public static short ReadInt16(this NetworkReader reader) => (short)reader.ReadUInt16();
|
||||||
public static ushort ReadUInt16(this NetworkReader reader) => reader.ReadBlittable<ushort>();
|
public static ushort ReadUInt16(this NetworkReader reader)
|
||||||
public static int ReadInt32(this NetworkReader reader) => reader.ReadBlittable<int>();
|
{
|
||||||
public static uint ReadUInt32(this NetworkReader reader) => reader.ReadBlittable<uint>();
|
ushort value = 0;
|
||||||
public static long ReadInt64(this NetworkReader reader) => reader.ReadBlittable<long>();
|
value |= reader.ReadByte();
|
||||||
public static ulong ReadUInt64(this NetworkReader reader) => reader.ReadBlittable<ulong>();
|
value |= (ushort)(reader.ReadByte() << 8);
|
||||||
public static float ReadSingle(this NetworkReader reader) => reader.ReadBlittable<float>();
|
return value;
|
||||||
public static double ReadDouble(this NetworkReader reader) => reader.ReadBlittable<double>();
|
}
|
||||||
public static decimal ReadDecimal(this NetworkReader reader) => reader.ReadBlittable<decimal>();
|
public static int ReadInt32(this NetworkReader reader) => (int)reader.ReadUInt32();
|
||||||
|
public static uint ReadUInt32(this NetworkReader reader)
|
||||||
|
{
|
||||||
|
uint value = 0;
|
||||||
|
value |= reader.ReadByte();
|
||||||
|
value |= (uint)(reader.ReadByte() << 8);
|
||||||
|
value |= (uint)(reader.ReadByte() << 16);
|
||||||
|
value |= (uint)(reader.ReadByte() << 24);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
public static long ReadInt64(this NetworkReader reader) => (long)reader.ReadUInt64();
|
||||||
|
public static ulong ReadUInt64(this NetworkReader reader)
|
||||||
|
{
|
||||||
|
ulong value = 0;
|
||||||
|
value |= reader.ReadByte();
|
||||||
|
value |= ((ulong)reader.ReadByte()) << 8;
|
||||||
|
value |= ((ulong)reader.ReadByte()) << 16;
|
||||||
|
value |= ((ulong)reader.ReadByte()) << 24;
|
||||||
|
value |= ((ulong)reader.ReadByte()) << 32;
|
||||||
|
value |= ((ulong)reader.ReadByte()) << 40;
|
||||||
|
value |= ((ulong)reader.ReadByte()) << 48;
|
||||||
|
value |= ((ulong)reader.ReadByte()) << 56;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
public static float ReadSingle(this NetworkReader reader)
|
||||||
|
{
|
||||||
|
UIntFloat converter = new UIntFloat();
|
||||||
|
converter.intValue = reader.ReadUInt32();
|
||||||
|
return converter.floatValue;
|
||||||
|
}
|
||||||
|
public static double ReadDouble(this NetworkReader reader)
|
||||||
|
{
|
||||||
|
UIntDouble converter = new UIntDouble();
|
||||||
|
converter.longValue = reader.ReadUInt64();
|
||||||
|
return converter.doubleValue;
|
||||||
|
}
|
||||||
|
public static decimal ReadDecimal(this NetworkReader reader)
|
||||||
|
{
|
||||||
|
UIntDecimal converter = new UIntDecimal();
|
||||||
|
converter.longValue1 = reader.ReadUInt64();
|
||||||
|
converter.longValue2 = reader.ReadUInt64();
|
||||||
|
return converter.decimalValue;
|
||||||
|
}
|
||||||
|
|
||||||
/// <exception cref="T:System.ArgumentException">if an invalid utf8 string is sent</exception>
|
/// <exception cref="T:System.ArgumentException">if an invalid utf8 string is sent</exception>
|
||||||
public static string ReadString(this NetworkReader reader)
|
public static string ReadString(this NetworkReader reader)
|
||||||
@ -256,18 +256,40 @@ public static ArraySegment<byte> ReadBytesAndSizeSegment(this NetworkReader read
|
|||||||
return count == 0 ? default : reader.ReadBytesSegment(checked((int)(count - 1u)));
|
return count == 0 ? default : reader.ReadBytesSegment(checked((int)(count - 1u)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Vector2 ReadVector2(this NetworkReader reader) => reader.ReadBlittable<Vector2>();
|
public static Vector2 ReadVector2(this NetworkReader reader) => new Vector2(reader.ReadSingle(), reader.ReadSingle());
|
||||||
public static Vector3 ReadVector3(this NetworkReader reader) => reader.ReadBlittable<Vector3>();
|
public static Vector3 ReadVector3(this NetworkReader reader) => new Vector3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
|
||||||
public static Vector4 ReadVector4(this NetworkReader reader) => reader.ReadBlittable<Vector4>();
|
public static Vector4 ReadVector4(this NetworkReader reader) => new Vector4(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
|
||||||
public static Vector2Int ReadVector2Int(this NetworkReader reader) => reader.ReadBlittable<Vector2Int>();
|
public static Vector2Int ReadVector2Int(this NetworkReader reader) => new Vector2Int(reader.ReadInt32(), reader.ReadInt32());
|
||||||
public static Vector3Int ReadVector3Int(this NetworkReader reader) => reader.ReadBlittable<Vector3Int>();
|
public static Vector3Int ReadVector3Int(this NetworkReader reader) => new Vector3Int(reader.ReadInt32(), reader.ReadInt32(), reader.ReadInt32());
|
||||||
public static Color ReadColor(this NetworkReader reader) => reader.ReadBlittable<Color>();
|
public static Color ReadColor(this NetworkReader reader) => new Color(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
|
||||||
public static Color32 ReadColor32(this NetworkReader reader) => reader.ReadBlittable<Color32>();
|
public static Color32 ReadColor32(this NetworkReader reader) => new Color32(reader.ReadByte(), reader.ReadByte(), reader.ReadByte(), reader.ReadByte());
|
||||||
public static Quaternion ReadQuaternion(this NetworkReader reader) => reader.ReadBlittable<Quaternion>();
|
public static Quaternion ReadQuaternion(this NetworkReader reader) => new Quaternion(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
|
||||||
public static Rect ReadRect(this NetworkReader reader) => reader.ReadBlittable<Rect>();
|
public static Rect ReadRect(this NetworkReader reader) => new Rect(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
|
||||||
public static Plane ReadPlane(this NetworkReader reader) => reader.ReadBlittable<Plane>();
|
public static Plane ReadPlane(this NetworkReader reader) => new Plane(reader.ReadVector3(), reader.ReadSingle());
|
||||||
public static Ray ReadRay(this NetworkReader reader) => reader.ReadBlittable<Ray>();
|
public static Ray ReadRay(this NetworkReader reader) => new Ray(reader.ReadVector3(), reader.ReadVector3());
|
||||||
public static Matrix4x4 ReadMatrix4x4(this NetworkReader reader) => reader.ReadBlittable<Matrix4x4>();
|
|
||||||
|
public static Matrix4x4 ReadMatrix4x4(this NetworkReader reader)
|
||||||
|
{
|
||||||
|
return new Matrix4x4
|
||||||
|
{
|
||||||
|
m00 = reader.ReadSingle(),
|
||||||
|
m01 = reader.ReadSingle(),
|
||||||
|
m02 = reader.ReadSingle(),
|
||||||
|
m03 = reader.ReadSingle(),
|
||||||
|
m10 = reader.ReadSingle(),
|
||||||
|
m11 = reader.ReadSingle(),
|
||||||
|
m12 = reader.ReadSingle(),
|
||||||
|
m13 = reader.ReadSingle(),
|
||||||
|
m20 = reader.ReadSingle(),
|
||||||
|
m21 = reader.ReadSingle(),
|
||||||
|
m22 = reader.ReadSingle(),
|
||||||
|
m23 = reader.ReadSingle(),
|
||||||
|
m30 = reader.ReadSingle(),
|
||||||
|
m31 = reader.ReadSingle(),
|
||||||
|
m32 = reader.ReadSingle(),
|
||||||
|
m33 = reader.ReadSingle()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public static byte[] ReadBytes(this NetworkReader reader, int count)
|
public static byte[] ReadBytes(this NetworkReader reader, int count)
|
||||||
{
|
{
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Unity.Collections.LowLevel.Unsafe;
|
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace Mirror
|
namespace Mirror
|
||||||
@ -139,85 +138,14 @@ public ArraySegment<byte> ToArraySegment()
|
|||||||
return new ArraySegment<byte>(buffer, 0, length);
|
return new ArraySegment<byte>(buffer, 0, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteBlittable<T> from DOTSNET.
|
public void WriteByte(byte value)
|
||||||
// Benchmark:
|
|
||||||
// WriteQuaternion x 100k, Macbook Pro 2015 @ 2.2Ghz, Unity 2018 LTS (debug mode)
|
|
||||||
//
|
|
||||||
// | Median | Min | Max | Avg | Std | (ms)
|
|
||||||
// before | 30.35 | 29.86 | 48.99 | 32.54 | 4.93 |
|
|
||||||
// blittable* | 5.69 | 5.52 | 27.51 | 7.78 | 5.65 |
|
|
||||||
//
|
|
||||||
// * without IsBlittable check
|
|
||||||
// => 4-6x faster!
|
|
||||||
//
|
|
||||||
// WriteQuaternion x 100k, Macbook Pro 2015 @ 2.2Ghz, Unity 2020.1 (release mode)
|
|
||||||
//
|
|
||||||
// | Median | Min | Max | Avg | Std | (ms)
|
|
||||||
// before | 9.41 | 8.90 | 23.02 | 10.72 | 3.07 |
|
|
||||||
// blittable* | 1.48 | 1.40 | 16.03 | 2.60 | 2.71 |
|
|
||||||
//
|
|
||||||
// * without IsBlittable check
|
|
||||||
// => 6x faster!
|
|
||||||
/// <summary>
|
|
||||||
/// Copies blittable type to buffer
|
|
||||||
/// <para>
|
|
||||||
/// This is extremely fast, but only works for blittable types.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// Note:
|
|
||||||
/// WriteBlittable assumes same endianness for server and client.
|
|
||||||
/// All Unity 2018+ platforms are little endian.<br/>
|
|
||||||
/// => run NetworkWriterTests.BlittableOnThisPlatform() to verify!
|
|
||||||
/// </para>
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// See <see href="https://docs.microsoft.com/en-us/dotnet/framework/interop/blittable-and-non-blittable-types">Blittable and Non-Blittable Types</see>
|
|
||||||
/// for more info.
|
|
||||||
/// </remarks>
|
|
||||||
/// <typeparam name="T">Needs to be unmanaged, see <see href="https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/unmanaged-types">unmanaged types</see></typeparam>
|
|
||||||
/// <param name="value"></param>
|
|
||||||
public unsafe void WriteBlittable<T>(T value)
|
|
||||||
where T : unmanaged
|
|
||||||
{
|
{
|
||||||
// check if blittable for safety
|
EnsureLength(position + 1);
|
||||||
#if UNITY_EDITOR
|
buffer[position++] = value;
|
||||||
if (!UnsafeUtility.IsBlittable(typeof(T)))
|
|
||||||
{
|
|
||||||
Debug.LogError(typeof(T) + " is not blittable!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
// calculate size
|
|
||||||
// sizeof(T) gets the managed size at compile time.
|
|
||||||
// Marshal.SizeOf<T> gets the unmanaged size at runtime (slow).
|
|
||||||
// => our 1mio writes benchmark is 6x slower with Marshal.SizeOf<T>
|
|
||||||
// => for blittable types, sizeof(T) is even recommended:
|
|
||||||
// https://docs.microsoft.com/en-us/dotnet/standard/native-interop/best-practices
|
|
||||||
int size = sizeof(T);
|
|
||||||
|
|
||||||
// ensure length
|
|
||||||
EnsureLength(position + size);
|
|
||||||
|
|
||||||
// write blittable
|
|
||||||
fixed (byte* ptr = &buffer[Position])
|
|
||||||
{
|
|
||||||
// cast buffer to T* pointer, then assign value to the area
|
|
||||||
*(T*)ptr = value;
|
|
||||||
}
|
|
||||||
Position += size;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// for byte arrays with consistent size, where the reader knows how many to read
|
||||||
/// <summary>
|
// (like a packet opcode that's always the same)
|
||||||
/// Copys bytes from given array to <see cref="buffer"/>
|
|
||||||
/// <para>
|
|
||||||
/// useful for byte arrays with consistent size, where the reader knows how many to read
|
|
||||||
/// (like a packet opcode that's always the same)
|
|
||||||
/// </para>
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="buffer"></param>
|
|
||||||
/// <param name="offset"></param>
|
|
||||||
/// <param name="count"></param>
|
|
||||||
public void WriteBytes(byte[] buffer, int offset, int count)
|
public void WriteBytes(byte[] buffer, int offset, int count)
|
||||||
{
|
{
|
||||||
EnsureLength(position + count);
|
EnsureLength(position + count);
|
||||||
@ -245,12 +173,8 @@ public void Write<T>(T value)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
// Mirror's Weaver automatically detects all NetworkWriter function types,
|
||||||
/// Built in Writer functions for Mirror
|
// but they do all need to be extensions.
|
||||||
/// <para>
|
|
||||||
/// Weaver automatically decects all extension methods for NetworkWriter
|
|
||||||
/// </para>
|
|
||||||
/// </summary>
|
|
||||||
public static class NetworkWriterExtensions
|
public static class NetworkWriterExtensions
|
||||||
{
|
{
|
||||||
static readonly ILogger logger = LogFactory.GetLogger(typeof(NetworkWriterExtensions));
|
static readonly ILogger logger = LogFactory.GetLogger(typeof(NetworkWriterExtensions));
|
||||||
@ -261,19 +185,76 @@ public static class NetworkWriterExtensions
|
|||||||
static readonly UTF8Encoding encoding = new UTF8Encoding(false, true);
|
static readonly UTF8Encoding encoding = new UTF8Encoding(false, true);
|
||||||
static readonly byte[] stringBuffer = new byte[NetworkWriter.MaxStringLength];
|
static readonly byte[] stringBuffer = new byte[NetworkWriter.MaxStringLength];
|
||||||
|
|
||||||
public static void WriteByte(this NetworkWriter writer, byte value) => writer.WriteBlittable(value);
|
public static void WriteByte(this NetworkWriter writer, byte value) => writer.WriteByte(value);
|
||||||
public static void WriteSByte(this NetworkWriter writer, sbyte value) => writer.WriteBlittable(value);
|
|
||||||
public static void WriteChar(this NetworkWriter writer, char value) => writer.WriteBlittable((short)value); // char isn't blittable
|
public static void WriteSByte(this NetworkWriter writer, sbyte value) => writer.WriteByte((byte)value);
|
||||||
public static void WriteBoolean(this NetworkWriter writer, bool value) => writer.WriteBlittable((byte)(value ? 1 : 0)); // bool isn't blittable
|
|
||||||
public static void WriteUInt16(this NetworkWriter writer, ushort value) => writer.WriteBlittable(value);
|
public static void WriteChar(this NetworkWriter writer, char value) => writer.WriteUInt16(value);
|
||||||
|
|
||||||
|
public static void WriteBoolean(this NetworkWriter writer, bool value) => writer.WriteByte((byte)(value ? 1 : 0));
|
||||||
|
|
||||||
|
public static void WriteUInt16(this NetworkWriter writer, ushort value)
|
||||||
|
{
|
||||||
|
writer.WriteByte((byte)value);
|
||||||
|
writer.WriteByte((byte)(value >> 8));
|
||||||
|
}
|
||||||
|
|
||||||
public static void WriteInt16(this NetworkWriter writer, short value) => writer.WriteUInt16((ushort)value);
|
public static void WriteInt16(this NetworkWriter writer, short value) => writer.WriteUInt16((ushort)value);
|
||||||
public static void WriteUInt32(this NetworkWriter writer, uint value) => writer.WriteBlittable(value);
|
|
||||||
public static void WriteInt32(this NetworkWriter writer, int value) => writer.WriteBlittable(value);
|
public static void WriteUInt32(this NetworkWriter writer, uint value)
|
||||||
public static void WriteUInt64(this NetworkWriter writer, ulong value) => writer.WriteBlittable(value);
|
{
|
||||||
public static void WriteInt64(this NetworkWriter writer, long value) => writer.WriteBlittable(value);
|
writer.WriteByte((byte)value);
|
||||||
public static void WriteSingle(this NetworkWriter writer, float value) => writer.WriteBlittable(value);
|
writer.WriteByte((byte)(value >> 8));
|
||||||
public static void WriteDouble(this NetworkWriter writer, double value) => writer.WriteBlittable(value);
|
writer.WriteByte((byte)(value >> 16));
|
||||||
public static void WriteDecimal(this NetworkWriter writer, decimal value) => writer.WriteBlittable(value);
|
writer.WriteByte((byte)(value >> 24));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void WriteInt32(this NetworkWriter writer, int value) => writer.WriteUInt32((uint)value);
|
||||||
|
|
||||||
|
public static void WriteUInt64(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 WriteInt64(this NetworkWriter writer, long value) => writer.WriteUInt64((ulong)value);
|
||||||
|
|
||||||
|
public static void WriteSingle(this NetworkWriter writer, float value)
|
||||||
|
{
|
||||||
|
UIntFloat converter = new UIntFloat
|
||||||
|
{
|
||||||
|
floatValue = value
|
||||||
|
};
|
||||||
|
writer.WriteUInt32(converter.intValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void WriteDouble(this NetworkWriter writer, double value)
|
||||||
|
{
|
||||||
|
UIntDouble converter = new UIntDouble
|
||||||
|
{
|
||||||
|
doubleValue = value
|
||||||
|
};
|
||||||
|
writer.WriteUInt64(converter.longValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
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.WriteUInt64(converter.longValue1);
|
||||||
|
writer.WriteUInt64(converter.longValue2);
|
||||||
|
}
|
||||||
|
|
||||||
public static void WriteString(this NetworkWriter writer, string value)
|
public static void WriteString(this NetworkWriter writer, string value)
|
||||||
{
|
{
|
||||||
@ -331,18 +312,103 @@ public static void WriteBytesAndSizeSegment(this NetworkWriter writer, ArraySegm
|
|||||||
writer.WriteBytesAndSize(buffer.Array, buffer.Offset, buffer.Count);
|
writer.WriteBytesAndSize(buffer.Array, buffer.Offset, buffer.Count);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void WriteVector2(this NetworkWriter writer, Vector2 value) => writer.WriteBlittable(value);
|
public static void WriteVector2(this NetworkWriter writer, Vector2 value)
|
||||||
public static void WriteVector3(this NetworkWriter writer, Vector3 value) => writer.WriteBlittable(value);
|
{
|
||||||
public static void WriteVector4(this NetworkWriter writer, Vector4 value) => writer.WriteBlittable(value);
|
writer.WriteSingle(value.x);
|
||||||
public static void WriteVector2Int(this NetworkWriter writer, Vector2Int value) => writer.WriteBlittable(value);
|
writer.WriteSingle(value.y);
|
||||||
public static void WriteVector3Int(this NetworkWriter writer, Vector3Int value) => writer.WriteBlittable(value);
|
}
|
||||||
public static void WriteColor(this NetworkWriter writer, Color value) => writer.WriteBlittable(value);
|
|
||||||
public static void WriteColor32(this NetworkWriter writer, Color32 value) => writer.WriteBlittable(value);
|
public static void WriteVector3(this NetworkWriter writer, Vector3 value)
|
||||||
public static void WriteQuaternion(this NetworkWriter writer, Quaternion value) => writer.WriteBlittable(value);
|
{
|
||||||
public static void WriteRect(this NetworkWriter writer, Rect value) => writer.WriteBlittable(value);
|
writer.WriteSingle(value.x);
|
||||||
public static void WritePlane(this NetworkWriter writer, Plane value) => writer.WriteBlittable(value);
|
writer.WriteSingle(value.y);
|
||||||
public static void WriteRay(this NetworkWriter writer, Ray value) => writer.WriteBlittable(value);
|
writer.WriteSingle(value.z);
|
||||||
public static void WriteMatrix4x4(this NetworkWriter writer, Matrix4x4 value) => writer.WriteBlittable(value);
|
}
|
||||||
|
|
||||||
|
public static void WriteVector4(this NetworkWriter writer, Vector4 value)
|
||||||
|
{
|
||||||
|
writer.WriteSingle(value.x);
|
||||||
|
writer.WriteSingle(value.y);
|
||||||
|
writer.WriteSingle(value.z);
|
||||||
|
writer.WriteSingle(value.w);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void WriteVector2Int(this NetworkWriter writer, Vector2Int value)
|
||||||
|
{
|
||||||
|
writer.WriteInt32(value.x);
|
||||||
|
writer.WriteInt32(value.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void WriteVector3Int(this NetworkWriter writer, Vector3Int value)
|
||||||
|
{
|
||||||
|
writer.WriteInt32(value.x);
|
||||||
|
writer.WriteInt32(value.y);
|
||||||
|
writer.WriteInt32(value.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void WriteColor(this NetworkWriter writer, Color value)
|
||||||
|
{
|
||||||
|
writer.WriteSingle(value.r);
|
||||||
|
writer.WriteSingle(value.g);
|
||||||
|
writer.WriteSingle(value.b);
|
||||||
|
writer.WriteSingle(value.a);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void WriteQuaternion(this NetworkWriter writer, Quaternion value)
|
||||||
|
{
|
||||||
|
writer.WriteSingle(value.x);
|
||||||
|
writer.WriteSingle(value.y);
|
||||||
|
writer.WriteSingle(value.z);
|
||||||
|
writer.WriteSingle(value.w);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void WriteRect(this NetworkWriter writer, Rect value)
|
||||||
|
{
|
||||||
|
writer.WriteSingle(value.xMin);
|
||||||
|
writer.WriteSingle(value.yMin);
|
||||||
|
writer.WriteSingle(value.width);
|
||||||
|
writer.WriteSingle(value.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void WritePlane(this NetworkWriter writer, Plane value)
|
||||||
|
{
|
||||||
|
writer.WriteVector3(value.normal);
|
||||||
|
writer.WriteSingle(value.distance);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void WriteRay(this NetworkWriter writer, Ray value)
|
||||||
|
{
|
||||||
|
writer.WriteVector3(value.origin);
|
||||||
|
writer.WriteVector3(value.direction);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void WriteMatrix4x4(this NetworkWriter writer, Matrix4x4 value)
|
||||||
|
{
|
||||||
|
writer.WriteSingle(value.m00);
|
||||||
|
writer.WriteSingle(value.m01);
|
||||||
|
writer.WriteSingle(value.m02);
|
||||||
|
writer.WriteSingle(value.m03);
|
||||||
|
writer.WriteSingle(value.m10);
|
||||||
|
writer.WriteSingle(value.m11);
|
||||||
|
writer.WriteSingle(value.m12);
|
||||||
|
writer.WriteSingle(value.m13);
|
||||||
|
writer.WriteSingle(value.m20);
|
||||||
|
writer.WriteSingle(value.m21);
|
||||||
|
writer.WriteSingle(value.m22);
|
||||||
|
writer.WriteSingle(value.m23);
|
||||||
|
writer.WriteSingle(value.m30);
|
||||||
|
writer.WriteSingle(value.m31);
|
||||||
|
writer.WriteSingle(value.m32);
|
||||||
|
writer.WriteSingle(value.m33);
|
||||||
|
}
|
||||||
|
|
||||||
public static void WriteGuid(this NetworkWriter writer, Guid value)
|
public static void WriteGuid(this NetworkWriter writer, Guid value)
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace Mirror
|
namespace Mirror
|
||||||
@ -31,4 +32,38 @@ public static class Channels
|
|||||||
public const int DefaultReliable = 0;
|
public const int DefaultReliable = 0;
|
||||||
public const int DefaultUnreliable = 1;
|
public const int DefaultUnreliable = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -- helpers for float conversion without allocations --
|
||||||
|
[StructLayout(LayoutKind.Explicit)]
|
||||||
|
internal struct UIntFloat
|
||||||
|
{
|
||||||
|
[FieldOffset(0)]
|
||||||
|
public float floatValue;
|
||||||
|
|
||||||
|
[FieldOffset(0)]
|
||||||
|
public uint intValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Explicit)]
|
||||||
|
internal struct UIntDouble
|
||||||
|
{
|
||||||
|
[FieldOffset(0)]
|
||||||
|
public double doubleValue;
|
||||||
|
|
||||||
|
[FieldOffset(0)]
|
||||||
|
public ulong longValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Explicit)]
|
||||||
|
internal struct UIntDecimal
|
||||||
|
{
|
||||||
|
[FieldOffset(0)]
|
||||||
|
public ulong longValue1;
|
||||||
|
|
||||||
|
[FieldOffset(8)]
|
||||||
|
public ulong longValue2;
|
||||||
|
|
||||||
|
[FieldOffset(0)]
|
||||||
|
public decimal decimalValue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
"Editor"
|
"Editor"
|
||||||
],
|
],
|
||||||
"excludePlatforms": [],
|
"excludePlatforms": [],
|
||||||
"allowUnsafeCode": true,
|
"allowUnsafeCode": false,
|
||||||
"overrideReferences": true,
|
"overrideReferences": true,
|
||||||
"precompiledReferences": [
|
"precompiledReferences": [
|
||||||
"NSubstitute.dll",
|
"NSubstitute.dll",
|
||||||
|
@ -26,45 +26,6 @@ public void Benchmark()
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
struct TestStruct
|
|
||||||
{
|
|
||||||
#pragma warning disable 649
|
|
||||||
public int data;
|
|
||||||
public byte data2;
|
|
||||||
#pragma warning restore 649
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public unsafe void BlittableOnThisPlatform()
|
|
||||||
{
|
|
||||||
// we assume NetworkWriter.WriteBlittable<T> to behave the same on
|
|
||||||
// all platforms:
|
|
||||||
// - need to be little endian (atm all Unity platforms are)
|
|
||||||
// - padded structs need to be same size across all platforms
|
|
||||||
// (C# int, byte, etc. should be same on all platforms, and
|
|
||||||
// C# should do the same padding on all platforms)
|
|
||||||
// https://kalapos.net/Blog/ShowPost/DotNetConceptOfTheWeek13_DotNetMemoryLayout
|
|
||||||
// => let's have a test that we can run on different platforms to
|
|
||||||
// be 100% sure
|
|
||||||
|
|
||||||
// let's assume little endian.
|
|
||||||
// it would also be ok if server and client are both big endian,
|
|
||||||
// but that's extremely unlikely.
|
|
||||||
Assert.That(BitConverter.IsLittleEndian, Is.True);
|
|
||||||
|
|
||||||
// TestStruct biggest member is 'int' = 4 bytes.
|
|
||||||
// so C# aligns it to 4 bytes, hence does padding for the byte:
|
|
||||||
// 0 int
|
|
||||||
// 1 int
|
|
||||||
// 2 int
|
|
||||||
// 3 int
|
|
||||||
// 4 byte
|
|
||||||
// 5 padding
|
|
||||||
// 6 padding
|
|
||||||
// 7 padding
|
|
||||||
Assert.That(sizeof(TestStruct), Is.EqualTo(8));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestWritingSmallMessage()
|
public void TestWritingSmallMessage()
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user