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": [],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": true,
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"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.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mirror
|
||||
@ -68,59 +75,13 @@ public NetworkReader(ArraySegment<byte> segment)
|
||||
buffer = segment;
|
||||
}
|
||||
|
||||
// ReadBlittable<T> from DOTSNET
|
||||
// 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
|
||||
public byte ReadByte()
|
||||
{
|
||||
// check if blittable for safety
|
||||
#if UNITY_EDITOR
|
||||
if (!UnsafeUtility.IsBlittable(typeof(T)))
|
||||
if (Position + 1 > buffer.Count)
|
||||
{
|
||||
throw new ArgumentException(typeof(T) + " is not blittable!");
|
||||
throw new EndOfStreamException("ReadByte out of range:" + ToString());
|
||||
}
|
||||
#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);
|
||||
|
||||
// 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;
|
||||
return buffer.Array[buffer.Offset + Position++];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -183,12 +144,9 @@ public T Read<T>()
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Built in Reader functions for Mirror
|
||||
/// <para>
|
||||
/// Weaver automatically detects all extension methods for NetworkWriter
|
||||
/// </para>
|
||||
/// </summary>
|
||||
|
||||
// Mirror's Weaver automatically detects all NetworkReader function types,
|
||||
// but they do all need to be extensions.
|
||||
public static class NetworkReaderExtensions
|
||||
{
|
||||
static readonly ILogger logger = LogFactory.GetLogger(typeof(NetworkReaderExtensions));
|
||||
@ -198,19 +156,61 @@ public static class NetworkReaderExtensions
|
||||
// 1000 readers after: 0.8MB GC, 18ms
|
||||
static readonly UTF8Encoding encoding = new UTF8Encoding(false, true);
|
||||
|
||||
public static byte ReadByte(this NetworkReader reader) => reader.ReadBlittable<byte>();
|
||||
public static sbyte ReadSByte(this NetworkReader reader) => reader.ReadBlittable<sbyte>();
|
||||
public static char ReadChar(this NetworkReader reader) => (char)reader.ReadBlittable<short>(); // char isn't blittable
|
||||
public static bool ReadBoolean(this NetworkReader reader) => reader.ReadBlittable<byte>() != 0; // bool isn't blittable
|
||||
public static short ReadInt16(this NetworkReader reader) => reader.ReadBlittable<short>();
|
||||
public static ushort ReadUInt16(this NetworkReader reader) => reader.ReadBlittable<ushort>();
|
||||
public static int ReadInt32(this NetworkReader reader) => reader.ReadBlittable<int>();
|
||||
public static uint ReadUInt32(this NetworkReader reader) => reader.ReadBlittable<uint>();
|
||||
public static long ReadInt64(this NetworkReader reader) => reader.ReadBlittable<long>();
|
||||
public static ulong ReadUInt64(this NetworkReader reader) => reader.ReadBlittable<ulong>();
|
||||
public static float ReadSingle(this NetworkReader reader) => reader.ReadBlittable<float>();
|
||||
public static double ReadDouble(this NetworkReader reader) => reader.ReadBlittable<double>();
|
||||
public static decimal ReadDecimal(this NetworkReader reader) => reader.ReadBlittable<decimal>();
|
||||
public static byte ReadByte(this NetworkReader reader) => reader.ReadByte();
|
||||
public static sbyte ReadSByte(this NetworkReader reader) => (sbyte)reader.ReadByte();
|
||||
public static char ReadChar(this NetworkReader reader) => (char)reader.ReadUInt16();
|
||||
public static bool ReadBoolean(this NetworkReader reader) => reader.ReadByte() != 0;
|
||||
public static short ReadInt16(this NetworkReader reader) => (short)reader.ReadUInt16();
|
||||
public static ushort ReadUInt16(this NetworkReader reader)
|
||||
{
|
||||
ushort value = 0;
|
||||
value |= reader.ReadByte();
|
||||
value |= (ushort)(reader.ReadByte() << 8);
|
||||
return value;
|
||||
}
|
||||
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>
|
||||
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)));
|
||||
}
|
||||
|
||||
public static Vector2 ReadVector2(this NetworkReader reader) => reader.ReadBlittable<Vector2>();
|
||||
public static Vector3 ReadVector3(this NetworkReader reader) => reader.ReadBlittable<Vector3>();
|
||||
public static Vector4 ReadVector4(this NetworkReader reader) => reader.ReadBlittable<Vector4>();
|
||||
public static Vector2Int ReadVector2Int(this NetworkReader reader) => reader.ReadBlittable<Vector2Int>();
|
||||
public static Vector3Int ReadVector3Int(this NetworkReader reader) => reader.ReadBlittable<Vector3Int>();
|
||||
public static Color ReadColor(this NetworkReader reader) => reader.ReadBlittable<Color>();
|
||||
public static Color32 ReadColor32(this NetworkReader reader) => reader.ReadBlittable<Color32>();
|
||||
public static Quaternion ReadQuaternion(this NetworkReader reader) => reader.ReadBlittable<Quaternion>();
|
||||
public static Rect ReadRect(this NetworkReader reader) => reader.ReadBlittable<Rect>();
|
||||
public static Plane ReadPlane(this NetworkReader reader) => reader.ReadBlittable<Plane>();
|
||||
public static Ray ReadRay(this NetworkReader reader) => reader.ReadBlittable<Ray>();
|
||||
public static Matrix4x4 ReadMatrix4x4(this NetworkReader reader) => reader.ReadBlittable<Matrix4x4>();
|
||||
public static Vector2 ReadVector2(this NetworkReader reader) => new Vector2(reader.ReadSingle(), reader.ReadSingle());
|
||||
public static Vector3 ReadVector3(this NetworkReader reader) => new Vector3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
|
||||
public static Vector4 ReadVector4(this NetworkReader reader) => new Vector4(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
|
||||
public static Vector2Int ReadVector2Int(this NetworkReader reader) => new Vector2Int(reader.ReadInt32(), reader.ReadInt32());
|
||||
public static Vector3Int ReadVector3Int(this NetworkReader reader) => new Vector3Int(reader.ReadInt32(), reader.ReadInt32(), reader.ReadInt32());
|
||||
public static Color ReadColor(this NetworkReader reader) => new Color(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
|
||||
public static Color32 ReadColor32(this NetworkReader reader) => new Color32(reader.ReadByte(), reader.ReadByte(), reader.ReadByte(), reader.ReadByte());
|
||||
public static Quaternion ReadQuaternion(this NetworkReader reader) => new Quaternion(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
|
||||
public static Rect ReadRect(this NetworkReader reader) => new Rect(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
|
||||
public static Plane ReadPlane(this NetworkReader reader) => new Plane(reader.ReadVector3(), reader.ReadSingle());
|
||||
public static Ray ReadRay(this NetworkReader reader) => new Ray(reader.ReadVector3(), reader.ReadVector3());
|
||||
|
||||
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)
|
||||
{
|
||||
|
@ -2,7 +2,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mirror
|
||||
@ -139,85 +138,14 @@ public ArraySegment<byte> ToArraySegment()
|
||||
return new ArraySegment<byte>(buffer, 0, length);
|
||||
}
|
||||
|
||||
// WriteBlittable<T> from DOTSNET.
|
||||
// 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
|
||||
public void WriteByte(byte value)
|
||||
{
|
||||
// check if blittable for safety
|
||||
#if UNITY_EDITOR
|
||||
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;
|
||||
EnsureLength(position + 1);
|
||||
buffer[position++] = value;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 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>
|
||||
// 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 WriteBytes(byte[] buffer, int offset, int count)
|
||||
{
|
||||
EnsureLength(position + count);
|
||||
@ -245,12 +173,8 @@ public void Write<T>(T value)
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Built in Writer functions for Mirror
|
||||
/// <para>
|
||||
/// Weaver automatically decects all extension methods for NetworkWriter
|
||||
/// </para>
|
||||
/// </summary>
|
||||
// Mirror's Weaver automatically detects all NetworkWriter function types,
|
||||
// but they do all need to be extensions.
|
||||
public static class 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 byte[] stringBuffer = new byte[NetworkWriter.MaxStringLength];
|
||||
|
||||
public static void WriteByte(this NetworkWriter writer, byte value) => writer.WriteBlittable(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 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 WriteByte(this NetworkWriter writer, byte value) => writer.WriteByte(value);
|
||||
|
||||
public static void WriteSByte(this NetworkWriter writer, sbyte value) => writer.WriteByte((byte)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 WriteUInt32(this NetworkWriter writer, uint value) => writer.WriteBlittable(value);
|
||||
public static void WriteInt32(this NetworkWriter writer, int value) => writer.WriteBlittable(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);
|
||||
public static void WriteSingle(this NetworkWriter writer, float value) => writer.WriteBlittable(value);
|
||||
public static void WriteDouble(this NetworkWriter writer, double value) => writer.WriteBlittable(value);
|
||||
public static void WriteDecimal(this NetworkWriter writer, decimal value) => writer.WriteBlittable(value);
|
||||
|
||||
public static void WriteUInt32(this NetworkWriter writer, uint value)
|
||||
{
|
||||
writer.WriteByte((byte)value);
|
||||
writer.WriteByte((byte)(value >> 8));
|
||||
writer.WriteByte((byte)(value >> 16));
|
||||
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)
|
||||
{
|
||||
@ -331,18 +312,103 @@ public static void WriteBytesAndSizeSegment(this NetworkWriter writer, ArraySegm
|
||||
writer.WriteBytesAndSize(buffer.Array, buffer.Offset, buffer.Count);
|
||||
}
|
||||
|
||||
public static void WriteVector2(this NetworkWriter writer, Vector2 value) => writer.WriteBlittable(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);
|
||||
public static void WriteVector2Int(this NetworkWriter writer, Vector2Int value) => writer.WriteBlittable(value);
|
||||
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 WriteQuaternion(this NetworkWriter writer, Quaternion value) => writer.WriteBlittable(value);
|
||||
public static void WriteRect(this NetworkWriter writer, Rect value) => writer.WriteBlittable(value);
|
||||
public static void WritePlane(this NetworkWriter writer, Plane value) => writer.WriteBlittable(value);
|
||||
public static void WriteRay(this NetworkWriter writer, Ray value) => writer.WriteBlittable(value);
|
||||
public static void WriteMatrix4x4(this NetworkWriter writer, Matrix4x4 value) => writer.WriteBlittable(value);
|
||||
public static void WriteVector2(this NetworkWriter writer, Vector2 value)
|
||||
{
|
||||
writer.WriteSingle(value.x);
|
||||
writer.WriteSingle(value.y);
|
||||
}
|
||||
|
||||
public static void WriteVector3(this NetworkWriter writer, Vector3 value)
|
||||
{
|
||||
writer.WriteSingle(value.x);
|
||||
writer.WriteSingle(value.y);
|
||||
writer.WriteSingle(value.z);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mirror
|
||||
@ -31,4 +32,38 @@ public static class Channels
|
||||
public const int DefaultReliable = 0;
|
||||
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"
|
||||
],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": true,
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": true,
|
||||
"precompiledReferences": [
|
||||
"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]
|
||||
public void TestWritingSmallMessage()
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user