Initial Commit
This commit is contained in:
parent
53eb92e9af
commit
270ab7d11f
15341 changed files with 700234 additions and 0 deletions
|
@ -0,0 +1,57 @@
|
|||
namespace MLAPI.Serialization
|
||||
{
|
||||
/// <summary>
|
||||
/// Arithmetic helper class
|
||||
/// </summary>
|
||||
public static class Arithmetic
|
||||
{
|
||||
// Sign bits for different data types
|
||||
internal const long SIGN_BIT_64 = -9223372036854775808;
|
||||
internal const int SIGN_BIT_32 = -2147483648;
|
||||
internal const short SIGN_BIT_16 = -32768;
|
||||
internal const sbyte SIGN_BIT_8 = -128;
|
||||
|
||||
// Ceiling function that doesn't deal with floating point values
|
||||
// these only work correctly with possitive numbers
|
||||
internal static ulong CeilingExact(ulong u1, ulong u2) => (u1 + u2 - 1) / u2;
|
||||
internal static long CeilingExact(long u1, long u2) => (u1 + u2 - 1) / u2;
|
||||
internal static uint CeilingExact(uint u1, uint u2) => (u1 + u2 - 1) / u2;
|
||||
internal static int CeilingExact(int u1, int u2) => (u1 + u2 - 1) / u2;
|
||||
internal static ushort CeilingExact(ushort u1, ushort u2) => (ushort)((u1 + u2 - 1) / u2);
|
||||
internal static short CeilingExact(short u1, short u2) => (short)((u1 + u2 - 1) / u2);
|
||||
internal static byte CeilingExact(byte u1, byte u2) => (byte)((u1 + u2 - 1) / u2);
|
||||
internal static sbyte CeilingExact(sbyte u1, sbyte u2) => (sbyte)((u1 + u2 - 1) / u2);
|
||||
|
||||
/// <summary>
|
||||
/// ZigZag encodes a signed integer and maps it to a unsigned integer
|
||||
/// </summary>
|
||||
/// <param name="value">The signed integer to encode</param>
|
||||
/// <returns>A ZigZag encoded version of the integer</returns>
|
||||
public static ulong ZigZagEncode(long value) => (ulong)((value >> 63) ^ (value << 1));
|
||||
|
||||
/// <summary>
|
||||
/// Decides a ZigZag encoded integer back to a signed integer
|
||||
/// </summary>
|
||||
/// <param name="value">The unsigned integer</param>
|
||||
/// <returns>The signed version of the integer</returns>
|
||||
public static long ZigZagDecode(ulong value) => (((long)(value >> 1) & 0x7FFFFFFFFFFFFFFFL) ^ ((long)(value << 63) >> 63));
|
||||
|
||||
/// <summary>
|
||||
/// Gets the output size in bytes after VarInting a unsigned integer
|
||||
/// </summary>
|
||||
/// <param name="value">The unsigned integer whose length to get</param>
|
||||
/// <returns>The amount of bytes</returns>
|
||||
public static int VarIntSize(ulong value) =>
|
||||
value <= 240 ? 1 :
|
||||
value <= 2287 ? 2 :
|
||||
value <= 67823 ? 3 :
|
||||
value <= 16777215 ? 4 :
|
||||
value <= 4294967295 ? 5 :
|
||||
value <= 1099511627775 ? 6 :
|
||||
value <= 281474976710655 ? 7 :
|
||||
value <= 72057594037927935 ? 8 :
|
||||
9;
|
||||
|
||||
internal static long Div8Ceil(ulong value) => (long)((value >> 3) + ((value & 1UL) | ((value >> 1) & 1UL) | ((value >> 2) & 1UL)));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 4e08651e5a4a45549926322c8957dfaa
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,38 @@
|
|||
namespace MLAPI.Serialization
|
||||
{
|
||||
/// <summary>
|
||||
/// AutoBitWritable implements INetworkSerializable and automatically serializes fields using reflection
|
||||
/// </summary>
|
||||
public abstract class AutoNetworkSerializable : INetworkSerializable
|
||||
{
|
||||
private void Write(NetworkWriter writer)
|
||||
{
|
||||
var fields = SerializationManager.GetFieldsForType(GetType());
|
||||
for (int i = 0; i < fields.Length; i++)
|
||||
{
|
||||
writer.WriteObjectPacked(fields[i].GetValue(this));
|
||||
}
|
||||
}
|
||||
|
||||
private void Read(NetworkReader reader)
|
||||
{
|
||||
var fields = SerializationManager.GetFieldsForType(GetType());
|
||||
for (int i = 0; i < fields.Length; i++)
|
||||
{
|
||||
fields[i].SetValue(this, reader.ReadObjectPacked(fields[i].FieldType));
|
||||
}
|
||||
}
|
||||
|
||||
public void NetworkSerialize(NetworkSerializer serializer)
|
||||
{
|
||||
if (serializer.IsReading)
|
||||
{
|
||||
Read(serializer.Reader);
|
||||
}
|
||||
else
|
||||
{
|
||||
Write(serializer.Writer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ffcc1ff68a0732d41b80725a9d665e1c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,7 @@
|
|||
namespace MLAPI.Serialization
|
||||
{
|
||||
public interface INetworkSerializable
|
||||
{
|
||||
void NetworkSerialize(NetworkSerializer serializer);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: dcbf989721df344779c6c845cf79444f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 0a4a44dfad228154db7945948c115efa
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,33 @@
|
|||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace MLAPI.Serialization
|
||||
{
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
internal struct ByteBool
|
||||
{
|
||||
[FieldOffset(0)]
|
||||
public bool BoolValue;
|
||||
|
||||
[FieldOffset(0)]
|
||||
public byte ByteValue;
|
||||
|
||||
public byte Collapse() =>
|
||||
ByteValue = (byte)((
|
||||
// Collapse all bits to position 1 and reassign as bit
|
||||
(ByteValue >> 7) |
|
||||
(ByteValue >> 6) |
|
||||
(ByteValue >> 5) |
|
||||
(ByteValue >> 4) |
|
||||
(ByteValue >> 3) |
|
||||
(ByteValue >> 2) |
|
||||
(ByteValue >> 1) |
|
||||
ByteValue
|
||||
) & 1);
|
||||
|
||||
public byte Collapse(bool b)
|
||||
{
|
||||
BoolValue = b;
|
||||
return Collapse();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a92848ab7f6eed144b5247f7b06bbbb0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,26 @@
|
|||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace MLAPI.Serialization
|
||||
{
|
||||
/// <summary>
|
||||
/// A struct with a explicit memory layout. The struct has 4 fields. float,uint,double and ulong.
|
||||
/// Every field has the same starting point in memory. If you insert a float value, it can be extracted as a uint.
|
||||
/// This is to allow for lockless and garbage free conversion from float to uint and double to ulong.
|
||||
/// This allows for VarInt encoding and other integer encodings.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
internal struct UIntFloat
|
||||
{
|
||||
[FieldOffset(0)]
|
||||
public float FloatValue;
|
||||
|
||||
[FieldOffset(0)]
|
||||
public uint UIntValue;
|
||||
|
||||
[FieldOffset(0)]
|
||||
public double DoubleValue;
|
||||
|
||||
[FieldOffset(0)]
|
||||
public ulong ULongValue;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b04241ccff0cf024d81c9d62d24fe077
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,512 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using static MLAPI.Serialization.Arithmetic;
|
||||
|
||||
namespace MLAPI.Serialization
|
||||
{
|
||||
/// <summary>
|
||||
/// A buffer that can be used at the bit level
|
||||
/// </summary>
|
||||
public class NetworkBuffer : Stream
|
||||
{
|
||||
private const int k_InitialCapacity = 16;
|
||||
private const float k_InitialGrowthFactor = 2.0f;
|
||||
|
||||
private byte[] m_Target;
|
||||
|
||||
/// <summary>
|
||||
/// A buffer that supports writing data smaller than a single byte. This buffer also has a built-in compression algorithm that can (optionally) be used to write compressed data.
|
||||
/// </summary>
|
||||
/// <param name="capacity">Initial capacity of buffer in bytes.</param>
|
||||
/// <param name="growthFactor">Factor by which buffer should grow when necessary.</param>
|
||||
public NetworkBuffer(int capacity, float growthFactor)
|
||||
{
|
||||
m_Target = new byte[capacity];
|
||||
GrowthFactor = growthFactor;
|
||||
Resizable = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A buffer that supports writing data smaller than a single byte. This buffer also has a built-in compression algorithm that can (optionally) be used to write compressed data.
|
||||
/// </summary>
|
||||
/// <param name="growthFactor">Factor by which buffer should grow when necessary.</param>
|
||||
public NetworkBuffer(float growthFactor) : this(k_InitialCapacity, growthFactor) { }
|
||||
|
||||
/// <summary>
|
||||
/// A buffer that supports writing data smaller than a single byte. This buffer also has a built-in compression algorithm that can (optionally) be used to write compressed data.
|
||||
/// </summary>
|
||||
/// <param name="capacity"></param>
|
||||
public NetworkBuffer(int capacity) : this(capacity, k_InitialGrowthFactor) { }
|
||||
|
||||
/// <summary>
|
||||
/// A buffer that supports writing data smaller than a single byte. This buffer also has a built-in compression algorithm that can (optionally) be used to write compressed data.
|
||||
/// </summary>
|
||||
public NetworkBuffer() : this(k_InitialCapacity, k_InitialGrowthFactor) { }
|
||||
|
||||
/// <summary>
|
||||
/// A buffer that supports writing data smaller than a single byte. This buffer also has a built-in compression algorithm that can (optionally) be used to write compressed data.
|
||||
/// NOTE: when using a pre-allocated buffer, the buffer will not grow!
|
||||
/// </summary>
|
||||
/// <param name="target">Pre-allocated buffer to write to</param>
|
||||
public NetworkBuffer(byte[] target)
|
||||
{
|
||||
m_Target = target;
|
||||
Resizable = false;
|
||||
BitLength = (ulong)(target.Length << 3);
|
||||
}
|
||||
|
||||
internal void SetTarget(byte[] target)
|
||||
{
|
||||
m_Target = target;
|
||||
BitLength = (ulong)(target.Length << 3);
|
||||
Position = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not the buffer will grow the buffer to accomodate more data.
|
||||
/// </summary>
|
||||
public bool Resizable { get; }
|
||||
|
||||
private float m_GrowthFactor;
|
||||
|
||||
/// <summary>
|
||||
/// Factor by which buffer should grow when necessary.
|
||||
/// </summary>
|
||||
public float GrowthFactor { set { m_GrowthFactor = value <= 1 ? 1.5f : value; } get { return m_GrowthFactor; } }
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not buffeer supports reading. (Always true)
|
||||
/// </summary>
|
||||
public override bool CanRead => true;
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not or there is any data to be read from the buffer.
|
||||
/// </summary>
|
||||
public bool HasDataToRead => Position < Length;
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not seeking is supported by this buffer. (Always true)
|
||||
/// </summary>
|
||||
public override bool CanSeek => true;
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not this buffer can accept new data. NOTE: this will return true even if only fewer than 8 bits can be written!
|
||||
/// </summary>
|
||||
public override bool CanWrite => !BitAligned || Position < m_Target.LongLength || Resizable;
|
||||
|
||||
/// <summary>
|
||||
/// Current buffer size. The buffer will not be resized (if possible) until Position is equal to Capacity and an attempt to write data is made.
|
||||
/// </summary>
|
||||
public long Capacity
|
||||
{
|
||||
get => m_Target.LongLength; // Optimized CeilingExact
|
||||
set
|
||||
{
|
||||
if (value < Length) throw new ArgumentOutOfRangeException("New capcity too small!");
|
||||
SetCapacity(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The current length of data considered to be "written" to the buffer.
|
||||
/// </summary>
|
||||
public override long Length { get => Div8Ceil(BitLength); }
|
||||
|
||||
/// <summary>
|
||||
/// The index that will be written to when any call to write data is made to this buffer.
|
||||
/// </summary>
|
||||
public override long Position { get => (long)(BitPosition >> 3); set => BitPosition = (ulong)value << 3; }
|
||||
|
||||
/// <summary>
|
||||
/// Bit offset into the buffer that new data will be written to.
|
||||
/// </summary>
|
||||
public ulong BitPosition { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Length of data (in bits) that is considered to be written to the buffer.
|
||||
/// </summary>
|
||||
public ulong BitLength { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not the current BitPosition is evenly divisible by 8. I.e. whether or not the BitPosition is at a byte boundary.
|
||||
/// </summary>
|
||||
public bool BitAligned { get => (BitPosition & 7) == 0; }
|
||||
|
||||
/// <summary>
|
||||
/// Flush buffer. This does nothing since data is written directly to a byte buffer.
|
||||
/// </summary>
|
||||
public override void Flush() { } // NOP
|
||||
|
||||
/// <summary>
|
||||
/// Grow buffer if possible. According to Max(bufferLength, 1) * growthFactor^Ceil(newContent/Max(bufferLength, 1))
|
||||
/// </summary>
|
||||
/// <param name="newContent">How many new values need to be accomodated (at least).</param>
|
||||
//private void Grow(long newContent) => SetCapacity(Math.Max(target.LongLength, 1) * (long)Math.Pow(GrowthFactor, CeilingExact(newContent, Math.Max(target.LongLength, 1))));
|
||||
/*
|
||||
private void Grow(long newContent)
|
||||
{
|
||||
float grow = newContent / 64;
|
||||
if (((long)grow) != grow) grow += 1;
|
||||
SetCapacity((Capacity + 64) * (long)grow);
|
||||
}
|
||||
*/
|
||||
private void Grow(long newContent)
|
||||
{
|
||||
long value = newContent + Capacity;
|
||||
long newCapacity = value;
|
||||
|
||||
if (newCapacity < 256) newCapacity = 256;
|
||||
// We are ok with this overflowing since the next statement will deal
|
||||
// with the cases where _capacity*2 overflows.
|
||||
if (newCapacity < Capacity * 2) newCapacity = Capacity * 2;
|
||||
// We want to expand the array up to Array.MaxArrayLengthOneDimensional
|
||||
// And we want to give the user the value that they asked for
|
||||
if ((uint)(Capacity * 2) > int.MaxValue) newCapacity = value > int.MaxValue ? value : int.MaxValue;
|
||||
|
||||
SetCapacity(newCapacity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a misaligned byte. WARNING: If the current BitPosition <strong>isn't</strong> byte misaligned,
|
||||
/// avoid using this method as it <strong>may</strong> cause an IndexOutOfBoundsException in such a case.
|
||||
/// </summary>
|
||||
/// <returns>A byte extracted from up to two separate buffer indices.</returns>
|
||||
private byte ReadByteMisaligned()
|
||||
{
|
||||
int mod = (int)(BitPosition & 7);
|
||||
return (byte)((m_Target[(int)Position] >> mod) | (m_Target[(int)(BitPosition += 8) >> 3] << (8 - mod)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read an aligned byte from the buffer. It's recommended to not use this when the BitPosition is byte-misaligned.
|
||||
/// </summary>
|
||||
/// <returns>The byte stored at the current Position index</returns>
|
||||
private byte ReadByteAligned() => m_Target[Position++];
|
||||
|
||||
/// <summary>
|
||||
/// Read a byte as a byte. This is just for internal use so as to minimize casts (cuz they ugly af).
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
internal byte ReadByteInternal() => BitAligned ? ReadByteAligned() : ReadByteMisaligned();
|
||||
|
||||
/// <summary>
|
||||
/// Read a byte from the buffer. This takes into account possible byte misalignment.
|
||||
/// </summary>
|
||||
/// <returns>A byte from the buffer or, if a byte can't be read, -1.</returns>
|
||||
public override int ReadByte() => HasDataToRead ? BitAligned ? ReadByteAligned() : ReadByteMisaligned() : -1;
|
||||
|
||||
/// <summary>
|
||||
/// Peeks a byte without advancing the position
|
||||
/// </summary>
|
||||
/// <returns>The peeked byte</returns>
|
||||
public int PeekByte() =>
|
||||
HasDataToRead
|
||||
? BitAligned ? m_Target[Position] :
|
||||
(byte)((m_Target[(int)Position] >> (int)(BitPosition & 7)) | (m_Target[(int)(BitPosition + 8) >> 3] << (8 - (int)(BitPosition & 7))))
|
||||
: -1;
|
||||
|
||||
/// <summary>
|
||||
/// Read a single bit from the buffer.
|
||||
/// </summary>
|
||||
/// <returns>A bit in bool format. (True represents 1, False represents 0)</returns>
|
||||
public bool ReadBit() => (m_Target[Position] & (1 << (int)(BitPosition++ & 7))) != 0;
|
||||
|
||||
/// <summary>
|
||||
/// Read a subset of the buffer buffer and write the contents to the supplied buffer.
|
||||
/// </summary>
|
||||
/// <param name="buffer">Buffer to copy data to.</param>
|
||||
/// <param name="offset">Offset into the buffer to write data to.</param>
|
||||
/// <param name="count">How many bytes to attempt to read.</param>
|
||||
/// <returns>Amount of bytes read.</returns>
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
int tLen = Math.Min(count, (int)(m_Target.LongLength - Position) - ((BitPosition & 7) == 0 ? 0 : 1));
|
||||
for (int i = 0; i < tLen; ++i) buffer[offset + i] = ReadByteInternal();
|
||||
return tLen;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set position in buffer to read from/write to.
|
||||
/// </summary>
|
||||
/// <param name="offset">Offset from position origin.</param>
|
||||
/// <param name="origin">How to calculate offset.</param>
|
||||
/// <returns>The new position in the buffer that data will be written to.</returns>
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
return (long)((
|
||||
BitPosition =
|
||||
(
|
||||
origin == SeekOrigin.Current ? offset > 0 ? Math.Min(BitPosition + ((ulong)offset << 3), (ulong)m_Target.Length << 3) :
|
||||
(offset ^ SIGN_BIT_64) > Position ? 0UL :
|
||||
BitPosition - (ulong)((offset ^ SIGN_BIT_64) << 3) :
|
||||
origin == SeekOrigin.Begin ? (ulong)Math.Max(0, offset) << 3 :
|
||||
(ulong)Math.Max(m_Target.Length - offset, 0) << 3
|
||||
)) >> 3) + (long)((BitPosition & 1UL) | ((BitPosition >> 1) & 1UL) | ((BitPosition >> 2) & 1UL));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the capacity of the internal buffer.
|
||||
/// </summary>
|
||||
/// <param name="value">New capacity of the buffer</param>
|
||||
private void SetCapacity(long value)
|
||||
{
|
||||
if (!Resizable) throw new NotSupportedException("Can't resize non resizable buffer"); // Don't do shit because fuck you (comment by @GabrielTofvesson -TwoTen)
|
||||
byte[] newTarg = new byte[value];
|
||||
long len = Math.Min(value, m_Target.LongLength);
|
||||
Buffer.BlockCopy(m_Target, 0, newTarg, 0, (int)len);
|
||||
if (value < m_Target.LongLength) BitPosition = (ulong)value << 3;
|
||||
m_Target = newTarg;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set length of data considered to be "written" to the buffer.
|
||||
/// </summary>
|
||||
/// <param name="value">New length of the written data.</param>
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
if (value < 0) throw new IndexOutOfRangeException("Cannot set a negative length!");
|
||||
if (value > Capacity) Grow(value - Capacity);
|
||||
BitLength = (ulong)value << 3;
|
||||
BitPosition = Math.Min((ulong)value << 3, BitPosition);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write data from the given buffer to the internal buffer.
|
||||
/// </summary>
|
||||
/// <param name="buffer">Buffer to write from.</param>
|
||||
/// <param name="offset">Offset in given buffer to start reading from.</param>
|
||||
/// <param name="count">Amount of bytes to read copy from given buffer to buffer.</param>
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
// Check bit alignment. If misaligned, each byte written has to be misaligned
|
||||
if (BitAligned)
|
||||
{
|
||||
if (Position + count >= m_Target.Length) Grow(count);
|
||||
Buffer.BlockCopy(buffer, offset, m_Target, (int)Position, count);
|
||||
Position += count;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Position + count + 1 >= m_Target.Length) Grow(count);
|
||||
for (int i = 0; i < count; ++i) WriteMisaligned(buffer[offset + i]);
|
||||
}
|
||||
|
||||
if (BitPosition > BitLength) BitLength = BitPosition;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write byte value to the internal buffer.
|
||||
/// </summary>
|
||||
/// <param name="value">The byte value to write.</param>
|
||||
public override void WriteByte(byte value)
|
||||
{
|
||||
// Check bit alignment. If misaligned, each byte written has to be misaligned
|
||||
if (BitAligned)
|
||||
{
|
||||
if (Position + 1 >= m_Target.Length) Grow(1);
|
||||
m_Target[Position] = value;
|
||||
Position += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Position + 1 + 1 >= m_Target.Length) Grow(1);
|
||||
WriteMisaligned(value);
|
||||
}
|
||||
|
||||
if (BitPosition > BitLength) BitLength = BitPosition;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a misaligned byte. NOTE: Using this when the bit position isn't byte-misaligned may cause an IndexOutOfBoundsException! This does not update the current Length of the buffer.
|
||||
/// </summary>
|
||||
/// <param name="value">Value to write</param>
|
||||
private void WriteMisaligned(byte value)
|
||||
{
|
||||
int off = (int)(BitPosition & 7);
|
||||
int shift1 = 8 - off;
|
||||
m_Target[Position + 1] = (byte)((m_Target[Position + 1] & (0xFF << off)) | (value >> shift1));
|
||||
m_Target[Position] = (byte)((m_Target[Position] & (0xFF >> shift1)) | (value << off));
|
||||
|
||||
BitPosition += 8;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Write a byte (in an int format) to the buffer. This does not update the current Length of the buffer.
|
||||
/// </summary>
|
||||
/// <param name="value">Value to write</param>
|
||||
private void WriteIntByte(int value) => WriteBytePrivate((byte)value);
|
||||
|
||||
/// <summary>
|
||||
/// Write a byte (in a ulong format) to the buffer. This does not update the current Length of the buffer.
|
||||
/// </summary>
|
||||
/// <param name="byteValue">Value to write</param>
|
||||
private void WriteULongByte(ulong byteValue) => WriteBytePrivate((byte)byteValue);
|
||||
|
||||
/// <summary>
|
||||
/// Write a byte to the buffer. This does not update the current Length of the buffer.
|
||||
/// </summary>
|
||||
/// <param name="value">Value to write</param>
|
||||
private void WriteBytePrivate(byte value)
|
||||
{
|
||||
if (Div8Ceil(BitPosition) == m_Target.LongLength) Grow(1);
|
||||
if (BitAligned)
|
||||
{
|
||||
m_Target[Position] = value;
|
||||
BitPosition += 8;
|
||||
}
|
||||
else WriteMisaligned(value);
|
||||
|
||||
UpdateLength();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write data from the given buffer to the internal buffer.
|
||||
/// </summary>
|
||||
/// <param name="buffer">Buffer to write from.</param>
|
||||
public void Write(byte[] buffer) => Write(buffer, 0, buffer.Length);
|
||||
|
||||
/// <summary>
|
||||
/// Write a single bit to the buffer
|
||||
/// </summary>
|
||||
/// <param name="bit">Value of the bit. True represents 1, False represents 0</param>
|
||||
public void WriteBit(bool bit)
|
||||
{
|
||||
if (BitAligned && Position == m_Target.Length) Grow(1);
|
||||
int offset = (int)(BitPosition & 7);
|
||||
long pos = Position;
|
||||
++BitPosition;
|
||||
m_Target[pos] = (byte)(bit ? (m_Target[pos] & ~(1 << offset)) | (1 << offset) : (m_Target[pos] & ~(1 << offset)));
|
||||
UpdateLength();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copy data from another stream
|
||||
/// </summary>
|
||||
/// <param name="s">Stream to copy from</param>
|
||||
/// <param name="count">How many bytes to read. Set to value less than one to read until ReadByte returns -1</param>
|
||||
public void CopyFrom(Stream s, int count = -1)
|
||||
{
|
||||
if (s is NetworkBuffer b) Write(b.m_Target, 0, count < 0 ? (int)b.Length : count);
|
||||
else
|
||||
{
|
||||
long currentPosition = s.Position;
|
||||
s.Position = 0;
|
||||
|
||||
int read;
|
||||
bool readToEnd = count < 0;
|
||||
while ((readToEnd || count-- > 0) && (read = s.ReadByte()) != -1)
|
||||
WriteIntByte(read);
|
||||
UpdateLength();
|
||||
|
||||
s.Position = currentPosition;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies internal buffer to stream
|
||||
/// </summary>
|
||||
/// <param name="stream">The stream to copy to</param>
|
||||
/// <param name="count">The maximum amount of bytes to copy. Set to value less than one to copy the full length</param>
|
||||
#if !NET35
|
||||
public new void CopyTo(Stream stream, int count = -1)
|
||||
#else
|
||||
public void CopyTo(Stream stream, int count = -1)
|
||||
#endif
|
||||
{
|
||||
stream.Write(m_Target, 0, count < 0 ? (int)Length : count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies urnead bytes from the source stream
|
||||
/// </summary>
|
||||
/// <param name="s">The source stream to copy from</param>
|
||||
/// <param name="count">The max amount of bytes to copy</param>
|
||||
public void CopyUnreadFrom(Stream s, int count = -1)
|
||||
{
|
||||
long currentPosition = s.Position;
|
||||
|
||||
int read;
|
||||
bool readToEnd = count < 0;
|
||||
while ((readToEnd || count-- > 0) && (read = s.ReadByte()) != -1) WriteIntByte(read);
|
||||
UpdateLength();
|
||||
|
||||
s.Position = currentPosition;
|
||||
}
|
||||
|
||||
// TODO: Implement CopyFrom() for NetworkBuffer with bitCount parameter
|
||||
/// <summary>
|
||||
/// Copys the bits from the provided NetworkBuffer
|
||||
/// </summary>
|
||||
/// <param name="buffer">The buffer to copy from</param>
|
||||
/// <param name="dataCount">The amount of data evel</param>
|
||||
/// <param name="copyBits">Whether or not to copy at the bit level rather than the byte level</param>
|
||||
public void CopyFrom(NetworkBuffer buffer, int dataCount, bool copyBits)
|
||||
{
|
||||
if (!copyBits)
|
||||
{
|
||||
CopyFrom(buffer, dataCount);
|
||||
}
|
||||
else
|
||||
{
|
||||
ulong count = dataCount < 0 ? buffer.BitLength : (ulong)dataCount;
|
||||
if (buffer.BitLength < count) throw new IndexOutOfRangeException("Attempted to read more data than is available");
|
||||
Write(buffer.GetBuffer(), 0, (int)(count >> 3));
|
||||
for (int i = (int)(count & 7); i >= 0; --i) WriteBit(buffer.ReadBit());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update length of data considered to be "written" to the buffer.
|
||||
/// </summary>
|
||||
private void UpdateLength()
|
||||
{
|
||||
if (BitPosition > BitLength) BitLength = BitPosition;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the internal buffer being written to by this buffer.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public byte[] GetBuffer() => m_Target;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a copy of the internal buffer. This only contains the used bytes
|
||||
/// </summary>
|
||||
/// <returns>A copy of used bytes in the internal buffer</returns>
|
||||
public byte[] ToArray()
|
||||
{
|
||||
byte[] copy = new byte[Length];
|
||||
Buffer.BlockCopy(m_Target, 0, copy, 0, (int)Length);
|
||||
return copy;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes zeros to fill the last byte
|
||||
/// </summary>
|
||||
public void PadBuffer()
|
||||
{
|
||||
while (!BitAligned)
|
||||
{
|
||||
WriteBit(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads zeros until the the buffer is byte aligned
|
||||
/// </summary>
|
||||
public void SkipPadBits()
|
||||
{
|
||||
while (!BitAligned)
|
||||
{
|
||||
ReadBit();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns hex encoded version of the buffer
|
||||
/// </summary>
|
||||
/// <returns>Hex encoded version of the buffer</returns>
|
||||
public override string ToString() => BitConverter.ToString(m_Target, 0, (int)Length);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f6242c3ec07a9e5489d3cdd0cb083173
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 4b085e35addc96e41a064eba3674887b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,790 @@
|
|||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MLAPI.Serialization
|
||||
{
|
||||
public sealed class NetworkSerializer
|
||||
{
|
||||
private readonly NetworkReader m_Reader;
|
||||
private readonly NetworkWriter m_Writer;
|
||||
|
||||
public NetworkReader Reader => m_Reader;
|
||||
public NetworkWriter Writer => m_Writer;
|
||||
|
||||
public bool IsReading { get; }
|
||||
|
||||
public NetworkSerializer(NetworkReader reader)
|
||||
{
|
||||
m_Reader = reader;
|
||||
IsReading = true;
|
||||
}
|
||||
|
||||
public NetworkSerializer(NetworkWriter writer)
|
||||
{
|
||||
m_Writer = writer;
|
||||
IsReading = false;
|
||||
}
|
||||
|
||||
public void Serialize(ref bool value)
|
||||
{
|
||||
if (IsReading) value = m_Reader.ReadBool();
|
||||
else m_Writer.WriteBool(value);
|
||||
}
|
||||
|
||||
public void Serialize(ref char value)
|
||||
{
|
||||
if (IsReading) value = m_Reader.ReadCharPacked();
|
||||
else m_Writer.WriteCharPacked(value);
|
||||
}
|
||||
|
||||
public void Serialize(ref sbyte value)
|
||||
{
|
||||
if (IsReading) value = m_Reader.ReadSByte();
|
||||
else m_Writer.WriteSByte(value);
|
||||
}
|
||||
|
||||
public void Serialize(ref byte value)
|
||||
{
|
||||
if (IsReading) value = m_Reader.ReadByteDirect();
|
||||
else m_Writer.WriteByte(value);
|
||||
}
|
||||
|
||||
public void Serialize(ref short value)
|
||||
{
|
||||
if (IsReading) value = m_Reader.ReadInt16Packed();
|
||||
else m_Writer.WriteInt16Packed(value);
|
||||
}
|
||||
|
||||
public void Serialize(ref ushort value)
|
||||
{
|
||||
if (IsReading) value = m_Reader.ReadUInt16Packed();
|
||||
else m_Writer.WriteUInt16Packed(value);
|
||||
}
|
||||
|
||||
public void Serialize(ref int value)
|
||||
{
|
||||
if (IsReading) value = m_Reader.ReadInt32Packed();
|
||||
else m_Writer.WriteInt32Packed(value);
|
||||
}
|
||||
|
||||
public void Serialize(ref uint value)
|
||||
{
|
||||
if (IsReading) value = m_Reader.ReadUInt32Packed();
|
||||
else m_Writer.WriteUInt32Packed(value);
|
||||
}
|
||||
|
||||
public void Serialize(ref long value)
|
||||
{
|
||||
if (IsReading) value = m_Reader.ReadInt64Packed();
|
||||
else m_Writer.WriteInt64Packed(value);
|
||||
}
|
||||
|
||||
public void Serialize(ref ulong value)
|
||||
{
|
||||
if (IsReading) value = m_Reader.ReadUInt64Packed();
|
||||
else m_Writer.WriteUInt64Packed(value);
|
||||
}
|
||||
|
||||
public void Serialize(ref float value)
|
||||
{
|
||||
if (IsReading) value = m_Reader.ReadSinglePacked();
|
||||
else m_Writer.WriteSinglePacked(value);
|
||||
}
|
||||
|
||||
public void Serialize(ref double value)
|
||||
{
|
||||
if (IsReading) value = m_Reader.ReadDoublePacked();
|
||||
else m_Writer.WriteDoublePacked(value);
|
||||
}
|
||||
|
||||
public void Serialize(ref string value)
|
||||
{
|
||||
if (IsReading)
|
||||
{
|
||||
var isSet = m_Reader.ReadBool();
|
||||
value = isSet ? m_Reader.ReadStringPacked() : null;
|
||||
}
|
||||
else
|
||||
{
|
||||
var isSet = value != null;
|
||||
m_Writer.WriteBool(isSet);
|
||||
if (isSet)
|
||||
{
|
||||
m_Writer.WriteStringPacked(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Serialize(ref Color value)
|
||||
{
|
||||
if (IsReading) value = m_Reader.ReadColorPacked();
|
||||
else m_Writer.WriteColorPacked(value);
|
||||
}
|
||||
|
||||
public void Serialize(ref Color32 value)
|
||||
{
|
||||
if (IsReading) value = m_Reader.ReadColor32();
|
||||
else m_Writer.WriteColor32(value);
|
||||
}
|
||||
|
||||
public void Serialize(ref Vector2 value)
|
||||
{
|
||||
if (IsReading) value = m_Reader.ReadVector2Packed();
|
||||
else m_Writer.WriteVector2Packed(value);
|
||||
}
|
||||
|
||||
public void Serialize(ref Vector3 value)
|
||||
{
|
||||
if (IsReading) value = m_Reader.ReadVector3Packed();
|
||||
else m_Writer.WriteVector3Packed(value);
|
||||
}
|
||||
|
||||
public void Serialize(ref Vector4 value)
|
||||
{
|
||||
if (IsReading) value = m_Reader.ReadVector4Packed();
|
||||
else m_Writer.WriteVector4Packed(value);
|
||||
}
|
||||
|
||||
public void Serialize(ref Quaternion value)
|
||||
{
|
||||
if (IsReading) value = m_Reader.ReadRotationPacked();
|
||||
else m_Writer.WriteRotationPacked(value);
|
||||
}
|
||||
|
||||
public void Serialize(ref Ray value)
|
||||
{
|
||||
if (IsReading) value = m_Reader.ReadRayPacked();
|
||||
else m_Writer.WriteRayPacked(value);
|
||||
}
|
||||
|
||||
public void Serialize(ref Ray2D value)
|
||||
{
|
||||
if (IsReading) value = m_Reader.ReadRay2DPacked();
|
||||
else m_Writer.WriteRay2DPacked(value);
|
||||
}
|
||||
|
||||
public unsafe void Serialize<TEnum>(ref TEnum value) where TEnum : unmanaged, Enum
|
||||
{
|
||||
if (sizeof(TEnum) == sizeof(int))
|
||||
{
|
||||
if (IsReading)
|
||||
{
|
||||
int intValue = m_Reader.ReadInt32Packed();
|
||||
value = *(TEnum*)&intValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
TEnum enumValue = value;
|
||||
m_Writer.WriteInt32Packed(*(int*)&enumValue);
|
||||
}
|
||||
}
|
||||
else if (sizeof(TEnum) == sizeof(byte))
|
||||
{
|
||||
if (IsReading)
|
||||
{
|
||||
byte intValue = m_Reader.ReadByteDirect();
|
||||
value = *(TEnum*)&intValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
TEnum enumValue = value;
|
||||
m_Writer.WriteByte(*(byte*)&enumValue);
|
||||
}
|
||||
}
|
||||
else if (sizeof(TEnum) == sizeof(short))
|
||||
{
|
||||
if (IsReading)
|
||||
{
|
||||
short intValue = m_Reader.ReadInt16Packed();
|
||||
value = *(TEnum*)&intValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
TEnum enumValue = value;
|
||||
m_Writer.WriteInt16Packed(*(short*)&enumValue);
|
||||
}
|
||||
}
|
||||
else if (sizeof(TEnum) == sizeof(long))
|
||||
{
|
||||
if (IsReading)
|
||||
{
|
||||
long intValue = m_Reader.ReadInt64Packed();
|
||||
value = *(TEnum*)&intValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
TEnum enumValue = value;
|
||||
m_Writer.WriteInt64Packed(*(long*)&enumValue);
|
||||
}
|
||||
}
|
||||
else if (IsReading)
|
||||
{
|
||||
value = default;
|
||||
}
|
||||
}
|
||||
|
||||
public void Serialize(ref bool[] array)
|
||||
{
|
||||
if (IsReading)
|
||||
{
|
||||
var length = m_Reader.ReadInt32Packed();
|
||||
array = length > -1 ? new bool[length] : null;
|
||||
for (var i = 0; i < length; ++i)
|
||||
{
|
||||
array[i] = m_Reader.ReadBool();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var length = array?.Length ?? -1;
|
||||
m_Writer.WriteInt32Packed(length);
|
||||
for (var i = 0; i < length; ++i)
|
||||
{
|
||||
m_Writer.WriteBool(array[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Serialize(ref char[] array)
|
||||
{
|
||||
if (IsReading)
|
||||
{
|
||||
var length = m_Reader.ReadInt32Packed();
|
||||
array = length > -1 ? new char[length] : null;
|
||||
for (var i = 0; i < length; ++i)
|
||||
{
|
||||
array[i] = m_Reader.ReadCharPacked();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var length = array?.Length ?? -1;
|
||||
m_Writer.WriteInt32Packed(length);
|
||||
for (var i = 0; i < length; ++i)
|
||||
{
|
||||
m_Writer.WriteCharPacked(array[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Serialize(ref sbyte[] array)
|
||||
{
|
||||
if (IsReading)
|
||||
{
|
||||
var length = m_Reader.ReadInt32Packed();
|
||||
array = length > -1 ? new sbyte[length] : null;
|
||||
for (var i = 0; i < length; ++i)
|
||||
{
|
||||
array[i] = m_Reader.ReadSByte();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var length = array?.Length ?? -1;
|
||||
m_Writer.WriteInt32Packed(length);
|
||||
for (var i = 0; i < length; ++i)
|
||||
{
|
||||
m_Writer.WriteSByte(array[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Serialize(ref byte[] array)
|
||||
{
|
||||
if (IsReading)
|
||||
{
|
||||
var length = m_Reader.ReadInt32Packed();
|
||||
array = length > -1 ? new byte[length] : null;
|
||||
for (var i = 0; i < length; ++i)
|
||||
{
|
||||
array[i] = m_Reader.ReadByteDirect();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var length = array?.Length ?? -1;
|
||||
m_Writer.WriteInt32Packed(length);
|
||||
for (var i = 0; i < length; ++i)
|
||||
{
|
||||
m_Writer.WriteByte(array[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Serialize(ref short[] array)
|
||||
{
|
||||
if (IsReading)
|
||||
{
|
||||
var length = m_Reader.ReadInt32Packed();
|
||||
array = length > -1 ? new short[length] : null;
|
||||
for (var i = 0; i < length; ++i)
|
||||
{
|
||||
array[i] = m_Reader.ReadInt16Packed();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var length = array?.Length ?? -1;
|
||||
m_Writer.WriteInt32Packed(length);
|
||||
for (var i = 0; i < length; ++i)
|
||||
{
|
||||
m_Writer.WriteInt16Packed(array[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Serialize(ref ushort[] array)
|
||||
{
|
||||
if (IsReading)
|
||||
{
|
||||
var length = m_Reader.ReadInt32Packed();
|
||||
array = length > -1 ? new ushort[length] : null;
|
||||
for (var i = 0; i < length; ++i)
|
||||
{
|
||||
array[i] = m_Reader.ReadUInt16Packed();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var length = array?.Length ?? -1;
|
||||
m_Writer.WriteInt32Packed(length);
|
||||
for (var i = 0; i < length; ++i)
|
||||
{
|
||||
m_Writer.WriteUInt16Packed(array[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Serialize(ref int[] array)
|
||||
{
|
||||
if (IsReading)
|
||||
{
|
||||
var length = m_Reader.ReadInt32Packed();
|
||||
array = length > -1 ? new int[length] : null;
|
||||
for (var i = 0; i < length; ++i)
|
||||
{
|
||||
array[i] = m_Reader.ReadInt32Packed();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var length = array?.Length ?? -1;
|
||||
m_Writer.WriteInt32Packed(length);
|
||||
for (var i = 0; i < length; ++i)
|
||||
{
|
||||
m_Writer.WriteInt32Packed(array[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Serialize(ref uint[] array)
|
||||
{
|
||||
if (IsReading)
|
||||
{
|
||||
var length = m_Reader.ReadInt32Packed();
|
||||
array = length > -1 ? new uint[length] : null;
|
||||
for (var i = 0; i < length; ++i)
|
||||
{
|
||||
array[i] = m_Reader.ReadUInt32Packed();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var length = array?.Length ?? -1;
|
||||
m_Writer.WriteInt32Packed(length);
|
||||
for (var i = 0; i < length; ++i)
|
||||
{
|
||||
m_Writer.WriteUInt32Packed(array[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Serialize(ref long[] array)
|
||||
{
|
||||
if (IsReading)
|
||||
{
|
||||
var length = m_Reader.ReadInt32Packed();
|
||||
array = length > -1 ? new long[length] : null;
|
||||
for (var i = 0; i < length; ++i)
|
||||
{
|
||||
array[i] = m_Reader.ReadInt64Packed();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var length = array?.Length ?? -1;
|
||||
m_Writer.WriteInt32Packed(length);
|
||||
for (var i = 0; i < length; ++i)
|
||||
{
|
||||
m_Writer.WriteInt64Packed(array[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Serialize(ref ulong[] array)
|
||||
{
|
||||
if (IsReading)
|
||||
{
|
||||
var length = m_Reader.ReadInt32Packed();
|
||||
array = length > -1 ? new ulong[length] : null;
|
||||
for (var i = 0; i < length; ++i)
|
||||
{
|
||||
array[i] = m_Reader.ReadUInt64Packed();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var length = array?.Length ?? -1;
|
||||
m_Writer.WriteInt32Packed(length);
|
||||
for (var i = 0; i < length; ++i)
|
||||
{
|
||||
m_Writer.WriteUInt64Packed(array[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Serialize(ref float[] array)
|
||||
{
|
||||
if (IsReading)
|
||||
{
|
||||
var length = m_Reader.ReadInt32Packed();
|
||||
array = length > -1 ? new float[length] : null;
|
||||
for (var i = 0; i < length; ++i)
|
||||
{
|
||||
array[i] = m_Reader.ReadSinglePacked();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var length = array?.Length ?? -1;
|
||||
m_Writer.WriteInt32Packed(length);
|
||||
for (var i = 0; i < length; ++i)
|
||||
{
|
||||
m_Writer.WriteSinglePacked(array[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Serialize(ref double[] array)
|
||||
{
|
||||
if (IsReading)
|
||||
{
|
||||
var length = m_Reader.ReadInt32Packed();
|
||||
array = length > -1 ? new double[length] : null;
|
||||
for (var i = 0; i < length; ++i)
|
||||
{
|
||||
array[i] = m_Reader.ReadDoublePacked();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var length = array?.Length ?? -1;
|
||||
m_Writer.WriteInt32Packed(length);
|
||||
for (var i = 0; i < length; ++i)
|
||||
{
|
||||
m_Writer.WriteDoublePacked(array[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Serialize(ref string[] array)
|
||||
{
|
||||
if (IsReading)
|
||||
{
|
||||
var length = m_Reader.ReadInt32Packed();
|
||||
array = length > -1 ? new string[length] : null;
|
||||
for (var i = 0; i < length; ++i)
|
||||
{
|
||||
var isSet = m_Reader.ReadBool();
|
||||
array[i] = isSet ? m_Reader.ReadStringPacked() : null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var length = array?.Length ?? -1;
|
||||
m_Writer.WriteInt32Packed(length);
|
||||
for (var i = 0; i < length; ++i)
|
||||
{
|
||||
var isSet = array[i] != null;
|
||||
m_Writer.WriteBool(isSet);
|
||||
if (isSet)
|
||||
{
|
||||
m_Writer.WriteStringPacked(array[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Serialize(ref Color[] array)
|
||||
{
|
||||
if (IsReading)
|
||||
{
|
||||
var length = m_Reader.ReadInt32Packed();
|
||||
array = length > -1 ? new Color[length] : null;
|
||||
for (var i = 0; i < length; ++i)
|
||||
{
|
||||
array[i] = m_Reader.ReadColorPacked();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var length = array?.Length ?? -1;
|
||||
m_Writer.WriteInt32Packed(length);
|
||||
for (var i = 0; i < length; ++i)
|
||||
{
|
||||
m_Writer.WriteColorPacked(array[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Serialize(ref Color32[] array)
|
||||
{
|
||||
if (IsReading)
|
||||
{
|
||||
var length = m_Reader.ReadInt32Packed();
|
||||
array = length > -1 ? new Color32[length] : null;
|
||||
for (var i = 0; i < length; ++i)
|
||||
{
|
||||
array[i] = m_Reader.ReadColor32();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var length = array?.Length ?? -1;
|
||||
m_Writer.WriteInt32Packed(length);
|
||||
for (var i = 0; i < length; ++i)
|
||||
{
|
||||
m_Writer.WriteColor32(array[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Serialize(ref Vector2[] array)
|
||||
{
|
||||
if (IsReading)
|
||||
{
|
||||
var length = m_Reader.ReadInt32Packed();
|
||||
array = length > -1 ? new Vector2[length] : null;
|
||||
for (var i = 0; i < length; ++i)
|
||||
{
|
||||
array[i] = m_Reader.ReadVector2Packed();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var length = array?.Length ?? -1;
|
||||
m_Writer.WriteInt32Packed(length);
|
||||
for (var i = 0; i < length; ++i)
|
||||
{
|
||||
m_Writer.WriteVector2Packed(array[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Serialize(ref Vector3[] array)
|
||||
{
|
||||
if (IsReading)
|
||||
{
|
||||
var length = m_Reader.ReadInt32Packed();
|
||||
array = length > -1 ? new Vector3[length] : null;
|
||||
for (var i = 0; i < length; ++i)
|
||||
{
|
||||
array[i] = m_Reader.ReadVector3Packed();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var length = array?.Length ?? -1;
|
||||
m_Writer.WriteInt32Packed(length);
|
||||
for (var i = 0; i < length; ++i)
|
||||
{
|
||||
m_Writer.WriteVector3Packed(array[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Serialize(ref Vector4[] array)
|
||||
{
|
||||
if (IsReading)
|
||||
{
|
||||
var length = m_Reader.ReadInt32Packed();
|
||||
array = length > -1 ? new Vector4[length] : null;
|
||||
for (var i = 0; i < length; ++i)
|
||||
{
|
||||
array[i] = m_Reader.ReadVector4Packed();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var length = array?.Length ?? -1;
|
||||
m_Writer.WriteInt32Packed(length);
|
||||
for (var i = 0; i < length; ++i)
|
||||
{
|
||||
m_Writer.WriteVector4Packed(array[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Serialize(ref Quaternion[] array)
|
||||
{
|
||||
if (IsReading)
|
||||
{
|
||||
var length = m_Reader.ReadInt32Packed();
|
||||
array = length > -1 ? new Quaternion[length] : null;
|
||||
for (var i = 0; i < length; ++i)
|
||||
{
|
||||
array[i] = m_Reader.ReadRotationPacked();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var length = array?.Length ?? -1;
|
||||
m_Writer.WriteInt32Packed(length);
|
||||
for (var i = 0; i < length; ++i)
|
||||
{
|
||||
m_Writer.WriteRotationPacked(array[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Serialize(ref Ray[] array)
|
||||
{
|
||||
if (IsReading)
|
||||
{
|
||||
var length = m_Reader.ReadInt32Packed();
|
||||
array = length > -1 ? new Ray[length] : null;
|
||||
for (var i = 0; i < length; ++i)
|
||||
{
|
||||
array[i] = m_Reader.ReadRayPacked();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var length = array?.Length ?? -1;
|
||||
m_Writer.WriteInt32Packed(length);
|
||||
for (var i = 0; i < length; ++i)
|
||||
{
|
||||
m_Writer.WriteRayPacked(array[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Serialize(ref Ray2D[] array)
|
||||
{
|
||||
if (IsReading)
|
||||
{
|
||||
var length = m_Reader.ReadInt32Packed();
|
||||
array = length > -1 ? new Ray2D[length] : null;
|
||||
for (var i = 0; i < length; ++i)
|
||||
{
|
||||
array[i] = m_Reader.ReadRay2DPacked();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var length = array?.Length ?? -1;
|
||||
m_Writer.WriteInt32Packed(length);
|
||||
for (var i = 0; i < length; ++i)
|
||||
{
|
||||
m_Writer.WriteRay2DPacked(array[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe void Serialize<TEnum>(ref TEnum[] array) where TEnum : unmanaged, Enum
|
||||
{
|
||||
int length;
|
||||
if (IsReading)
|
||||
{
|
||||
length = m_Reader.ReadInt32Packed();
|
||||
array = length > -1 ? new TEnum[length] : null;
|
||||
}
|
||||
else
|
||||
{
|
||||
length = array?.Length ?? -1;
|
||||
m_Writer.WriteInt32Packed(length);
|
||||
}
|
||||
|
||||
if (sizeof(TEnum) == sizeof(int))
|
||||
{
|
||||
if (IsReading)
|
||||
{
|
||||
for (var i = 0; i < length; ++i)
|
||||
{
|
||||
int intValue = m_Reader.ReadInt32Packed();
|
||||
array[i] = *(TEnum*)&intValue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (var i = 0; i < length; ++i)
|
||||
{
|
||||
TEnum enumValue = array[i];
|
||||
m_Writer.WriteInt32Packed(*(int*)&enumValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (sizeof(TEnum) == sizeof(byte))
|
||||
{
|
||||
if (IsReading)
|
||||
{
|
||||
for (var i = 0; i < length; ++i)
|
||||
{
|
||||
byte intValue = m_Reader.ReadByteDirect();
|
||||
array[i] = *(TEnum*)&intValue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (var i = 0; i < length; ++i)
|
||||
{
|
||||
TEnum enumValue = array[i];
|
||||
m_Writer.WriteByte(*(byte*)&enumValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (sizeof(TEnum) == sizeof(short))
|
||||
{
|
||||
if (IsReading)
|
||||
{
|
||||
for (var i = 0; i < length; ++i)
|
||||
{
|
||||
short intValue = m_Reader.ReadInt16Packed();
|
||||
array[i] = *(TEnum*)&intValue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (var i = 0; i < length; ++i)
|
||||
{
|
||||
TEnum enumValue = array[i];
|
||||
m_Writer.WriteInt16Packed(*(short*)&enumValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (sizeof(TEnum) == sizeof(long))
|
||||
{
|
||||
if (IsReading)
|
||||
{
|
||||
for (var i = 0; i < length; ++i)
|
||||
{
|
||||
long intValue = m_Reader.ReadInt64Packed();
|
||||
array[i] = *(TEnum*)&intValue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (var i = 0; i < length; ++i)
|
||||
{
|
||||
TEnum enumValue = array[i];
|
||||
m_Writer.WriteInt64Packed(*(long*)&enumValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (IsReading)
|
||||
{
|
||||
array = default;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 371e8ff5255834ad7a262f2bf6034b21
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d5f796d542353ae43a8462806a5b98aa
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a40752eb876d6a64994fdd5eb5650fb7
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,90 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using MLAPI.Logging;
|
||||
|
||||
namespace MLAPI.Serialization.Pooled
|
||||
{
|
||||
/// <summary>
|
||||
/// Static class containing PooledNetworkBuffers
|
||||
/// </summary>
|
||||
public static class NetworkBufferPool
|
||||
{
|
||||
private static uint s_CreatedBuffers = 0;
|
||||
private static Queue<WeakReference> s_OverflowBuffers = new Queue<WeakReference>();
|
||||
private static Queue<PooledNetworkBuffer> s_Buffers = new Queue<PooledNetworkBuffer>();
|
||||
|
||||
private const uint k_MaxBitPoolBuffers = 1024;
|
||||
private const uint k_MaxCreatedDelta = 768;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves an expandable PooledNetworkBuffer from the pool
|
||||
/// </summary>
|
||||
/// <returns>An expandable PooledNetworkBuffer</returns>
|
||||
public static PooledNetworkBuffer GetBuffer()
|
||||
{
|
||||
if (s_Buffers.Count == 0)
|
||||
{
|
||||
if (s_OverflowBuffers.Count > 0)
|
||||
{
|
||||
if (NetworkLog.CurrentLogLevel <= LogLevel.Developer)
|
||||
{
|
||||
NetworkLog.LogInfo($"Retrieving {nameof(PooledNetworkBuffer)} from overflow pool. Recent burst?");
|
||||
}
|
||||
|
||||
object weakBuffer = null;
|
||||
while (s_OverflowBuffers.Count > 0 && ((weakBuffer = s_OverflowBuffers.Dequeue().Target) == null)) ;
|
||||
|
||||
if (weakBuffer != null)
|
||||
{
|
||||
PooledNetworkBuffer strongBuffer = (PooledNetworkBuffer)weakBuffer;
|
||||
|
||||
strongBuffer.SetLength(0);
|
||||
strongBuffer.Position = 0;
|
||||
|
||||
return strongBuffer;
|
||||
}
|
||||
}
|
||||
|
||||
if (s_CreatedBuffers == k_MaxBitPoolBuffers)
|
||||
{
|
||||
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) NetworkLog.LogWarning($"{k_MaxBitPoolBuffers} buffers have been created. Did you forget to dispose?");
|
||||
}
|
||||
else if (s_CreatedBuffers < k_MaxBitPoolBuffers) s_CreatedBuffers++;
|
||||
|
||||
return new PooledNetworkBuffer();
|
||||
}
|
||||
|
||||
PooledNetworkBuffer buffer = s_Buffers.Dequeue();
|
||||
buffer.SetLength(0);
|
||||
buffer.Position = 0;
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Puts a PooledNetworkBuffer back into the pool
|
||||
/// </summary>
|
||||
/// <param name="buffer">The buffer to put in the pool</param>
|
||||
public static void PutBackInPool(PooledNetworkBuffer buffer)
|
||||
{
|
||||
if (s_Buffers.Count > k_MaxCreatedDelta)
|
||||
{
|
||||
// The user just created lots of buffers without returning them in between.
|
||||
// Buffers are essentially byte array wrappers. This is valuable memory.
|
||||
// Thus we put this buffer as a weak reference incase of another burst
|
||||
// But still leave it to GC
|
||||
if (NetworkLog.CurrentLogLevel <= LogLevel.Developer)
|
||||
{
|
||||
NetworkLog.LogInfo($"Putting {nameof(PooledNetworkBuffer)} into overflow pool. Did you forget to dispose?");
|
||||
}
|
||||
|
||||
s_OverflowBuffers.Enqueue(new WeakReference(buffer));
|
||||
}
|
||||
else
|
||||
{
|
||||
s_Buffers.Enqueue(buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b12cc234a5c700041bb1f08f8229f676
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,52 @@
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using MLAPI.Logging;
|
||||
|
||||
namespace MLAPI.Serialization.Pooled
|
||||
{
|
||||
/// <summary>
|
||||
/// Static class containing PooledNetworkReaders
|
||||
/// </summary>
|
||||
public static class NetworkReaderPool
|
||||
{
|
||||
private static byte s_CreatedReaders = 0;
|
||||
private static Queue<PooledNetworkReader> s_Readers = new Queue<PooledNetworkReader>();
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a PooledNetworkReader
|
||||
/// </summary>
|
||||
/// <param name="stream">The stream the reader should read from</param>
|
||||
/// <returns>A PooledNetworkReader</returns>
|
||||
public static PooledNetworkReader GetReader(Stream stream)
|
||||
{
|
||||
if (s_Readers.Count == 0)
|
||||
{
|
||||
if (s_CreatedReaders == 254)
|
||||
{
|
||||
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) NetworkLog.LogWarning("255 readers have been created. Did you forget to dispose?");
|
||||
}
|
||||
else if (s_CreatedReaders < 255) s_CreatedReaders++;
|
||||
|
||||
return new PooledNetworkReader(stream);
|
||||
}
|
||||
|
||||
PooledNetworkReader reader = s_Readers.Dequeue();
|
||||
reader.SetStream(stream);
|
||||
|
||||
return reader;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Puts a PooledNetworkReader back into the pool
|
||||
/// </summary>
|
||||
/// <param name="reader">The reader to put in the pool</param>
|
||||
public static void PutBackInPool(PooledNetworkReader reader)
|
||||
{
|
||||
if (s_Readers.Count < 64) s_Readers.Enqueue(reader);
|
||||
else if (NetworkLog.CurrentLogLevel <= LogLevel.Developer)
|
||||
{
|
||||
NetworkLog.LogInfo($"{nameof(NetworkReaderPool)} already has 64 queued. Throwing to GC. Did you forget to dispose?");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 14a958cf00e46084693623b5c90ba657
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,52 @@
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using MLAPI.Logging;
|
||||
|
||||
namespace MLAPI.Serialization.Pooled
|
||||
{
|
||||
/// <summary>
|
||||
/// Static class containing PooledNetworkWriters
|
||||
/// </summary>
|
||||
public static class NetworkWriterPool
|
||||
{
|
||||
private static byte s_CreatedWriters = 0;
|
||||
private static Queue<PooledNetworkWriter> s_Writers = new Queue<PooledNetworkWriter>();
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a PooledNetworkWriter
|
||||
/// </summary>
|
||||
/// <param name="stream">The stream the writer should write to</param>
|
||||
/// <returns>A PooledNetworkWriter</returns>
|
||||
public static PooledNetworkWriter GetWriter(Stream stream)
|
||||
{
|
||||
if (s_Writers.Count == 0)
|
||||
{
|
||||
if (s_CreatedWriters == 254)
|
||||
{
|
||||
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) NetworkLog.LogWarning("255 writers have been created. Did you forget to dispose?");
|
||||
}
|
||||
else if (s_CreatedWriters < 255) s_CreatedWriters++;
|
||||
|
||||
return new PooledNetworkWriter(stream);
|
||||
}
|
||||
|
||||
PooledNetworkWriter writer = s_Writers.Dequeue();
|
||||
writer.SetStream(stream);
|
||||
|
||||
return writer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Puts a PooledNetworkWriter back into the pool
|
||||
/// </summary>
|
||||
/// <param name="writer">The writer to put in the pool</param>
|
||||
public static void PutBackInPool(PooledNetworkWriter writer)
|
||||
{
|
||||
if (s_Writers.Count < 64) s_Writers.Enqueue(writer);
|
||||
else if (NetworkLog.CurrentLogLevel <= LogLevel.Developer)
|
||||
{
|
||||
NetworkLog.LogInfo($"{nameof(NetworkWriterPool)} already has 64 queued. Throwing to GC. Did you forget to dispose?");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2cec275be55b22c4ba0fe27f74fbe2a9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,37 @@
|
|||
using System;
|
||||
|
||||
namespace MLAPI.Serialization.Pooled
|
||||
{
|
||||
/// <summary>
|
||||
/// Disposable NetworkBuffer that returns back to the NetworkBufferPool when disposed
|
||||
/// </summary>
|
||||
public sealed class PooledNetworkBuffer : NetworkBuffer, IDisposable
|
||||
{
|
||||
private bool isDisposed = false;
|
||||
|
||||
internal PooledNetworkBuffer() { }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a PooledNetworkBuffer from the static NetworkBufferPool
|
||||
/// </summary>
|
||||
/// <returns>PooledNetworkBuffer</returns>
|
||||
public static PooledNetworkBuffer Get()
|
||||
{
|
||||
var buffer = NetworkBufferPool.GetBuffer();
|
||||
buffer.isDisposed = false;
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the PooledNetworkBuffer into the static NetworkBufferPool
|
||||
/// </summary>
|
||||
public new void Dispose()
|
||||
{
|
||||
if (!isDisposed)
|
||||
{
|
||||
isDisposed = true;
|
||||
NetworkBufferPool.PutBackInPool(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 4b9ca4cb2f1e219419c81cc68ad3b9b1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,46 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MLAPI.Serialization.Pooled
|
||||
{
|
||||
/// <summary>
|
||||
/// Disposable NetworkReader that returns the Reader to the NetworkReaderPool when disposed
|
||||
/// </summary>
|
||||
public sealed class PooledNetworkReader : NetworkReader, IDisposable
|
||||
{
|
||||
private NetworkSerializer m_Serializer;
|
||||
public NetworkSerializer Serializer => m_Serializer ?? (m_Serializer = new NetworkSerializer(this));
|
||||
|
||||
private bool m_IsDisposed = false;
|
||||
|
||||
internal PooledNetworkReader(Stream stream) : base(stream) { }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a PooledNetworkReader from the static NetworkReaderPool
|
||||
/// </summary>
|
||||
/// <returns>PooledNetworkReader</returns>
|
||||
public static PooledNetworkReader Get(Stream stream)
|
||||
{
|
||||
var reader = NetworkReaderPool.GetReader(stream);
|
||||
reader.m_IsDisposed = false;
|
||||
return reader;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the PooledNetworkReader into the static NetworkReaderPool
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (!m_IsDisposed)
|
||||
{
|
||||
m_IsDisposed = true;
|
||||
NetworkReaderPool.PutBackInPool(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning("Disposing reader that thinks it is already disposed!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 31928243e74f75541b5e5c4ae87149db
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,46 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MLAPI.Serialization.Pooled
|
||||
{
|
||||
/// <summary>
|
||||
/// Disposable NetworkWriter that returns the Writer to the NetworkWriterPool when disposed
|
||||
/// </summary>
|
||||
public sealed class PooledNetworkWriter : NetworkWriter, IDisposable
|
||||
{
|
||||
private NetworkSerializer m_Serializer;
|
||||
public NetworkSerializer Serializer => m_Serializer ?? (m_Serializer = new NetworkSerializer(this));
|
||||
|
||||
private bool m_IsDisposed = false;
|
||||
|
||||
internal PooledNetworkWriter(Stream stream) : base(stream) { }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a PooledNetworkWriter from the static NetworkWriterPool
|
||||
/// </summary>
|
||||
/// <returns>PooledNetworkWriter</returns>
|
||||
public static PooledNetworkWriter Get(Stream stream)
|
||||
{
|
||||
var writer = NetworkWriterPool.GetWriter(stream);
|
||||
writer.m_IsDisposed = false;
|
||||
return writer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the PooledNetworkWriter into the static NetworkWriterPool
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (!m_IsDisposed)
|
||||
{
|
||||
m_IsDisposed = true;
|
||||
NetworkWriterPool.PutBackInPool(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("Writer is being disposed but thinks it is already disposed");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: fe89760ee0eccbe4dbf1a0e7cbb86a81
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,144 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using MLAPI.Reflection;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MLAPI.Serialization
|
||||
{
|
||||
/// <summary>
|
||||
/// Helper class to manage the MLAPI serialization.
|
||||
/// </summary>
|
||||
public static class SerializationManager
|
||||
{
|
||||
private static Dictionary<Type, FieldInfo[]> s_FieldCache = new Dictionary<Type, FieldInfo[]>();
|
||||
private static Dictionary<Type, BoxedSerializationDelegate> s_CachedExternalSerializers = new Dictionary<Type, BoxedSerializationDelegate>();
|
||||
private static Dictionary<Type, BoxedDeserializationDelegate> s_CachedExternalDeserializers = new Dictionary<Type, BoxedDeserializationDelegate>();
|
||||
|
||||
/// <summary>
|
||||
/// The delegate used when registering custom deserialization for a type.
|
||||
/// </summary>
|
||||
/// <param name="stream">The stream to read the data required to construct the type.</param>
|
||||
/// <typeparam name="T">The type to deserialize.</typeparam>
|
||||
public delegate T CustomDeserializationDelegate<T>(Stream stream);
|
||||
|
||||
/// <summary>
|
||||
/// The delegate used when registering custom serialization for a type.
|
||||
/// </summary>
|
||||
/// <param name="stream">The stream to write data to that is required to reconstruct the type in the deserialization delegate.</param>
|
||||
/// <param name="instance">The instance to serialize to the stream.</param>
|
||||
/// <typeparam name="T">The type to serialize.</typeparam>
|
||||
public delegate void CustomSerializationDelegate<T>(Stream stream, T instance);
|
||||
|
||||
// These two are what we use internally. They box the value.
|
||||
private delegate void BoxedSerializationDelegate(Stream stream, object instance);
|
||||
|
||||
private delegate object BoxedDeserializationDelegate(Stream stream);
|
||||
|
||||
/// <summary>
|
||||
/// Registers a custom serialization and deserialization pair for a object.
|
||||
/// This is useful for writing objects that are behind the third party wall. Such as .NET types.
|
||||
/// </summary>
|
||||
/// <param name="onSerialize">The delegate to invoke to serialize the type.</param>
|
||||
/// <param name="onDeserialize">The delegate to invoke to deserialize the type.</param>
|
||||
/// <typeparam name="T">The type to register.</typeparam>
|
||||
public static void RegisterSerializationHandlers<T>(CustomSerializationDelegate<T> onSerialize, CustomDeserializationDelegate<T> onDeserialize)
|
||||
{
|
||||
s_CachedExternalSerializers[typeof(T)] = (stream, instance) => onSerialize(stream, (T)instance);
|
||||
s_CachedExternalDeserializers[typeof(T)] = stream => onDeserialize(stream);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a serialization handler that was registered previously for a specific type.
|
||||
/// This will remove both the serialization and deserialization handler.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type for the serialization handlers to remove.</typeparam>
|
||||
/// <returns>Whether or not either the serialization or deserialization handlers for the type was removed.</returns>
|
||||
public static bool RemoveSerializationHandlers<T>()
|
||||
{
|
||||
bool serializationRemoval = s_CachedExternalSerializers.Remove(typeof(T));
|
||||
bool deserializationRemoval = s_CachedExternalDeserializers.Remove(typeof(T));
|
||||
|
||||
return serializationRemoval || deserializationRemoval;
|
||||
}
|
||||
|
||||
internal static bool TrySerialize(Stream stream, object obj)
|
||||
{
|
||||
if (s_CachedExternalSerializers.ContainsKey(obj.GetType()))
|
||||
{
|
||||
s_CachedExternalSerializers[obj.GetType()](stream, obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static bool TryDeserialize(Stream stream, Type type, out object obj)
|
||||
{
|
||||
if (s_CachedExternalDeserializers.ContainsKey(type))
|
||||
{
|
||||
obj = s_CachedExternalDeserializers[type](stream);
|
||||
return true;
|
||||
}
|
||||
|
||||
obj = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static FieldInfo[] GetFieldsForType(Type type)
|
||||
{
|
||||
if (s_FieldCache.ContainsKey(type)) return s_FieldCache[type];
|
||||
|
||||
FieldInfo[] fields = type
|
||||
.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
|
||||
.Where(x => (x.IsPublic || x.GetCustomAttributes(typeof(SerializeField), true).Length > 0) && IsTypeSupported(x.FieldType))
|
||||
.OrderBy(x => x.Name, StringComparer.Ordinal).ToArray();
|
||||
|
||||
s_FieldCache.Add(type, fields);
|
||||
|
||||
return fields;
|
||||
}
|
||||
|
||||
private static HashSet<Type> s_SupportedTypes = new HashSet<Type>()
|
||||
{
|
||||
typeof(byte),
|
||||
typeof(byte),
|
||||
typeof(sbyte),
|
||||
typeof(ushort),
|
||||
typeof(short),
|
||||
typeof(int),
|
||||
typeof(uint),
|
||||
typeof(long),
|
||||
typeof(ulong),
|
||||
typeof(float),
|
||||
typeof(double),
|
||||
typeof(string),
|
||||
typeof(bool),
|
||||
typeof(Vector2),
|
||||
typeof(Vector3),
|
||||
typeof(Vector4),
|
||||
typeof(Color),
|
||||
typeof(Color32),
|
||||
typeof(Ray),
|
||||
typeof(Quaternion),
|
||||
typeof(char),
|
||||
typeof(GameObject),
|
||||
typeof(NetworkObject),
|
||||
typeof(NetworkBehaviour)
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Returns if a type is supported for serialization
|
||||
/// </summary>
|
||||
/// <param name="type">The type to check</param>
|
||||
/// <returns>Whether or not the type is supported</returns>
|
||||
public static bool IsTypeSupported(Type type)
|
||||
{
|
||||
return type.IsEnum || s_SupportedTypes.Contains(type) || type.HasInterface(typeof(INetworkSerializable)) ||
|
||||
(s_CachedExternalSerializers.ContainsKey(type) && s_CachedExternalDeserializers.ContainsKey(type)) ||
|
||||
(type.IsArray && type.HasElementType && IsTypeSupported(type.GetElementType()));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 6f42870c1eac2da49aee0bc9057687b8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Loading…
Add table
Add a link
Reference in a new issue