This commit is contained in:
Karrar
2024-03-27 22:26:13 +03:00
commit 0caf47a728
223 changed files with 32253 additions and 0 deletions

View File

@@ -0,0 +1,150 @@
#if NETCOREAPP3_0_OR_GREATER || NETCOREAPP3_1 || NET5_0
using System;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics.X86;
#endif
#if NET5_0_OR_GREATER || NET5_0
using System.Runtime.Intrinsics.Arm;
#endif
namespace LiteNetLib.Utils
{
//Implementation from Crc32.NET
public static class CRC32C
{
public const int ChecksumSize = 4;
private const uint Poly = 0x82F63B78u;
private static readonly uint[] Table;
static CRC32C()
{
#if NETCOREAPP3_0_OR_GREATER || NETCOREAPP3_1 || NET5_0
if (Sse42.IsSupported)
return;
#endif
#if NET5_0_OR_GREATER || NET5_0
if (Crc32.IsSupported)
return;
#endif
Table = NetUtils.AllocatePinnedUninitializedArray<uint>(16 * 256);
for (uint i = 0; i < 256; i++)
{
uint res = i;
for (int t = 0; t < 16; t++)
{
for (int k = 0; k < 8; k++)
res = (res & 1) == 1 ? Poly ^ (res >> 1) : (res >> 1);
Table[t * 256 + i] = res;
}
}
}
/// <summary>
/// Compute CRC32C for data
/// </summary>
/// <param name="input">input data</param>
/// <param name="offset">offset</param>
/// <param name="length">length</param>
/// <returns>CRC32C checksum</returns>
public static uint Compute(byte[] input, int offset, int length)
{
uint crcLocal = uint.MaxValue;
#if NETCOREAPP3_0_OR_GREATER || NETCOREAPP3_1 || NET5_0
if (Sse42.IsSupported)
{
var data = new ReadOnlySpan<byte>(input, offset, length);
int processed = 0;
if (Sse42.X64.IsSupported && data.Length > sizeof(ulong))
{
processed = data.Length / sizeof(ulong) * sizeof(ulong);
var ulongs = MemoryMarshal.Cast<byte, ulong>(data.Slice(0, processed));
ulong crclong = crcLocal;
for (int i = 0; i < ulongs.Length; i++)
{
crclong = Sse42.X64.Crc32(crclong, ulongs[i]);
}
crcLocal = (uint)crclong;
}
else if (data.Length > sizeof(uint))
{
processed = data.Length / sizeof(uint) * sizeof(uint);
var uints = MemoryMarshal.Cast<byte, uint>(data.Slice(0, processed));
for (int i = 0; i < uints.Length; i++)
{
crcLocal = Sse42.Crc32(crcLocal, uints[i]);
}
}
for (int i = processed; i < data.Length; i++)
{
crcLocal = Sse42.Crc32(crcLocal, data[i]);
}
return crcLocal ^ uint.MaxValue;
}
#endif
#if NET5_0_OR_GREATER || NET5_0
if (Crc32.IsSupported)
{
var data = new ReadOnlySpan<byte>(input, offset, length);
int processed = 0;
if (Crc32.Arm64.IsSupported && data.Length > sizeof(ulong))
{
processed = data.Length / sizeof(ulong) * sizeof(ulong);
var ulongs = MemoryMarshal.Cast<byte, ulong>(data.Slice(0, processed));
for (int i = 0; i < ulongs.Length; i++)
{
crcLocal = Crc32.Arm64.ComputeCrc32C(crcLocal, ulongs[i]);
}
}
else if (data.Length > sizeof(uint))
{
processed = data.Length / sizeof(uint) * sizeof(uint);
var uints = MemoryMarshal.Cast<byte, uint>(data.Slice(0, processed));
for (int i = 0; i < uints.Length; i++)
{
crcLocal = Crc32.ComputeCrc32C(crcLocal, uints[i]);
}
}
for (int i = processed; i < data.Length; i++)
{
crcLocal = Crc32.ComputeCrc32C(crcLocal, data[i]);
}
return crcLocal ^ uint.MaxValue;
}
#endif
while (length >= 16)
{
var a = Table[(3 * 256) + input[offset + 12]]
^ Table[(2 * 256) + input[offset + 13]]
^ Table[(1 * 256) + input[offset + 14]]
^ Table[(0 * 256) + input[offset + 15]];
var b = Table[(7 * 256) + input[offset + 8]]
^ Table[(6 * 256) + input[offset + 9]]
^ Table[(5 * 256) + input[offset + 10]]
^ Table[(4 * 256) + input[offset + 11]];
var c = Table[(11 * 256) + input[offset + 4]]
^ Table[(10 * 256) + input[offset + 5]]
^ Table[(9 * 256) + input[offset + 6]]
^ Table[(8 * 256) + input[offset + 7]];
var d = Table[(15 * 256) + ((byte)crcLocal ^ input[offset])]
^ Table[(14 * 256) + ((byte)(crcLocal >> 8) ^ input[offset + 1])]
^ Table[(13 * 256) + ((byte)(crcLocal >> 16) ^ input[offset + 2])]
^ Table[(12 * 256) + ((crcLocal >> 24) ^ input[offset + 3])];
crcLocal = d ^ c ^ b ^ a;
offset += 16;
length -= 16;
}
while (--length >= 0)
crcLocal = Table[(byte)(crcLocal ^ input[offset++])] ^ crcLocal >> 8;
return crcLocal ^ uint.MaxValue;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 76883c4cc6dbb0a43b3a7755fd827824
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,175 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace LiteNetLib.Utils
{
public static class FastBitConverter
{
#if (LITENETLIB_UNSAFE || LITENETLIB_UNSAFELIB || NETCOREAPP3_1 || NET5_0 || NETCOREAPP3_0_OR_GREATER) && !BIGENDIAN
#if LITENETLIB_UNSAFE
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe void GetBytes<T>(byte[] bytes, int startIndex, T value) where T : unmanaged
{
int size = sizeof(T);
if (bytes.Length < startIndex + size)
ThrowIndexOutOfRangeException();
#if LITENETLIB_UNSAFELIB || NETCOREAPP3_1 || NET5_0 || NETCOREAPP3_0_OR_GREATER
Unsafe.As<byte, T>(ref bytes[startIndex]) = value;
#else
fixed (byte* ptr = &bytes[startIndex])
{
#if UNITY_ANDROID
// On some android systems, assigning *(T*)ptr throws a NRE if
// the ptr isn't aligned (i.e. if Position is 1,2,3,5, etc.).
// Here we have to use memcpy.
//
// => we can't get a pointer of a struct in C# without
// marshalling allocations
// => instead, we stack allocate an array of type T and use that
// => stackalloc avoids GC and is very fast. it only works for
// value types, but all blittable types are anyway.
T* valueBuffer = stackalloc T[1] { value };
UnsafeUtility.MemCpy(ptr, valueBuffer, size);
#else
*(T*)ptr = value;
#endif
}
#endif
}
#else
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void GetBytes<T>(byte[] bytes, int startIndex, T value) where T : unmanaged
{
if (bytes.Length < startIndex + Unsafe.SizeOf<T>())
ThrowIndexOutOfRangeException();
Unsafe.As<byte, T>(ref bytes[startIndex]) = value;
}
#endif
private static void ThrowIndexOutOfRangeException() => throw new IndexOutOfRangeException();
#else
[StructLayout(LayoutKind.Explicit)]
private struct ConverterHelperDouble
{
[FieldOffset(0)]
public ulong Along;
[FieldOffset(0)]
public double Adouble;
}
[StructLayout(LayoutKind.Explicit)]
private struct ConverterHelperFloat
{
[FieldOffset(0)]
public int Aint;
[FieldOffset(0)]
public float Afloat;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void WriteLittleEndian(byte[] buffer, int offset, ulong data)
{
#if BIGENDIAN
buffer[offset + 7] = (byte)(data);
buffer[offset + 6] = (byte)(data >> 8);
buffer[offset + 5] = (byte)(data >> 16);
buffer[offset + 4] = (byte)(data >> 24);
buffer[offset + 3] = (byte)(data >> 32);
buffer[offset + 2] = (byte)(data >> 40);
buffer[offset + 1] = (byte)(data >> 48);
buffer[offset ] = (byte)(data >> 56);
#else
buffer[offset] = (byte)(data);
buffer[offset + 1] = (byte)(data >> 8);
buffer[offset + 2] = (byte)(data >> 16);
buffer[offset + 3] = (byte)(data >> 24);
buffer[offset + 4] = (byte)(data >> 32);
buffer[offset + 5] = (byte)(data >> 40);
buffer[offset + 6] = (byte)(data >> 48);
buffer[offset + 7] = (byte)(data >> 56);
#endif
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void WriteLittleEndian(byte[] buffer, int offset, int data)
{
#if BIGENDIAN
buffer[offset + 3] = (byte)(data);
buffer[offset + 2] = (byte)(data >> 8);
buffer[offset + 1] = (byte)(data >> 16);
buffer[offset ] = (byte)(data >> 24);
#else
buffer[offset] = (byte)(data);
buffer[offset + 1] = (byte)(data >> 8);
buffer[offset + 2] = (byte)(data >> 16);
buffer[offset + 3] = (byte)(data >> 24);
#endif
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void WriteLittleEndian(byte[] buffer, int offset, short data)
{
#if BIGENDIAN
buffer[offset + 1] = (byte)(data);
buffer[offset ] = (byte)(data >> 8);
#else
buffer[offset] = (byte)(data);
buffer[offset + 1] = (byte)(data >> 8);
#endif
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void GetBytes(byte[] bytes, int startIndex, double value)
{
ConverterHelperDouble ch = new ConverterHelperDouble { Adouble = value };
WriteLittleEndian(bytes, startIndex, ch.Along);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void GetBytes(byte[] bytes, int startIndex, float value)
{
ConverterHelperFloat ch = new ConverterHelperFloat { Afloat = value };
WriteLittleEndian(bytes, startIndex, ch.Aint);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void GetBytes(byte[] bytes, int startIndex, short value)
{
WriteLittleEndian(bytes, startIndex, value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void GetBytes(byte[] bytes, int startIndex, ushort value)
{
WriteLittleEndian(bytes, startIndex, (short)value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void GetBytes(byte[] bytes, int startIndex, int value)
{
WriteLittleEndian(bytes, startIndex, value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void GetBytes(byte[] bytes, int startIndex, uint value)
{
WriteLittleEndian(bytes, startIndex, (int)value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void GetBytes(byte[] bytes, int startIndex, long value)
{
WriteLittleEndian(bytes, startIndex, (ulong)value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void GetBytes(byte[] bytes, int startIndex, ulong value)
{
WriteLittleEndian(bytes, startIndex, value);
}
#endif
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 62e022071d6c4284986276c32b92b32c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
namespace LiteNetLib.Utils
{
public interface INetSerializable
{
void Serialize(NetDataWriter writer);
void Deserialize(NetDataReader reader);
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 819c3f8d629972f49bdf77b1fc3d1c2e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,673 @@
using System;
using System.Net;
using System.Runtime.CompilerServices;
namespace LiteNetLib.Utils
{
public class NetDataReader
{
protected byte[] _data;
protected int _position;
protected int _dataSize;
private int _offset;
public byte[] RawData
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _data;
}
public int RawDataSize
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _dataSize;
}
public int UserDataOffset
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _offset;
}
public int UserDataSize
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _dataSize - _offset;
}
public bool IsNull
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _data == null;
}
public int Position
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _position;
}
public bool EndOfData
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _position == _dataSize;
}
public int AvailableBytes
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _dataSize - _position;
}
public void SkipBytes(int count)
{
_position += count;
}
public void SetPosition(int position)
{
_position = position;
}
public void SetSource(NetDataWriter dataWriter)
{
_data = dataWriter.Data;
_position = 0;
_offset = 0;
_dataSize = dataWriter.Length;
}
public void SetSource(byte[] source)
{
_data = source;
_position = 0;
_offset = 0;
_dataSize = source.Length;
}
public void SetSource(byte[] source, int offset, int maxSize)
{
_data = source;
_position = offset;
_offset = offset;
_dataSize = maxSize;
}
public NetDataReader()
{
}
public NetDataReader(NetDataWriter writer)
{
SetSource(writer);
}
public NetDataReader(byte[] source)
{
SetSource(source);
}
public NetDataReader(byte[] source, int offset, int maxSize)
{
SetSource(source, offset, maxSize);
}
#region GetMethods
public IPEndPoint GetNetEndPoint()
{
string host = GetString(1000);
int port = GetInt();
return NetUtils.MakeEndPoint(host, port);
}
public byte GetByte()
{
byte res = _data[_position];
_position++;
return res;
}
public sbyte GetSByte()
{
return (sbyte)GetByte();
}
public T[] GetArray<T>(ushort size)
{
ushort length = BitConverter.ToUInt16(_data, _position);
_position += 2;
T[] result = new T[length];
length *= size;
Buffer.BlockCopy(_data, _position, result, 0, length);
_position += length;
return result;
}
public bool[] GetBoolArray()
{
return GetArray<bool>(1);
}
public ushort[] GetUShortArray()
{
return GetArray<ushort>(2);
}
public short[] GetShortArray()
{
return GetArray<short>(2);
}
public int[] GetIntArray()
{
return GetArray<int>(4);
}
public uint[] GetUIntArray()
{
return GetArray<uint>(4);
}
public float[] GetFloatArray()
{
return GetArray<float>(4);
}
public double[] GetDoubleArray()
{
return GetArray<double>(8);
}
public long[] GetLongArray()
{
return GetArray<long>(8);
}
public ulong[] GetULongArray()
{
return GetArray<ulong>(8);
}
public string[] GetStringArray()
{
ushort length = GetUShort();
string[] arr = new string[length];
for (int i = 0; i < length; i++)
{
arr[i] = GetString();
}
return arr;
}
/// <summary>
/// Note that "maxStringLength" only limits the number of characters in a string, not its size in bytes.
/// Strings that exceed this parameter are returned as empty
/// </summary>
public string[] GetStringArray(int maxStringLength)
{
ushort length = GetUShort();
string[] arr = new string[length];
for (int i = 0; i < length; i++)
{
arr[i] = GetString(maxStringLength);
}
return arr;
}
public bool GetBool()
{
return GetByte() == 1;
}
public char GetChar()
{
return (char)GetUShort();
}
public ushort GetUShort()
{
ushort result = BitConverter.ToUInt16(_data, _position);
_position += 2;
return result;
}
public short GetShort()
{
short result = BitConverter.ToInt16(_data, _position);
_position += 2;
return result;
}
public long GetLong()
{
long result = BitConverter.ToInt64(_data, _position);
_position += 8;
return result;
}
public ulong GetULong()
{
ulong result = BitConverter.ToUInt64(_data, _position);
_position += 8;
return result;
}
public int GetInt()
{
int result = BitConverter.ToInt32(_data, _position);
_position += 4;
return result;
}
public uint GetUInt()
{
uint result = BitConverter.ToUInt32(_data, _position);
_position += 4;
return result;
}
public float GetFloat()
{
float result = BitConverter.ToSingle(_data, _position);
_position += 4;
return result;
}
public double GetDouble()
{
double result = BitConverter.ToDouble(_data, _position);
_position += 8;
return result;
}
/// <summary>
/// Note that "maxLength" only limits the number of characters in a string, not its size in bytes.
/// </summary>
/// <returns>"string.Empty" if value > "maxLength"</returns>
public string GetString(int maxLength)
{
ushort size = GetUShort();
if (size == 0)
{
return string.Empty;
}
int actualSize = size - 1;
if (actualSize >= NetDataWriter.StringBufferMaxLength)
{
return null;
}
ArraySegment<byte> data = GetBytesSegment(actualSize);
return (maxLength > 0 && NetDataWriter.uTF8Encoding.Value.GetCharCount(data.Array, data.Offset, data.Count) > maxLength) ?
string.Empty :
NetDataWriter.uTF8Encoding.Value.GetString(data.Array, data.Offset, data.Count);
}
public string GetString()
{
ushort size = GetUShort();
if (size == 0)
{
return string.Empty;
}
int actualSize = size - 1;
if (actualSize >= NetDataWriter.StringBufferMaxLength)
{
return null;
}
ArraySegment<byte> data = GetBytesSegment(actualSize);
return NetDataWriter.uTF8Encoding.Value.GetString(data.Array, data.Offset, data.Count);
}
public ArraySegment<byte> GetBytesSegment(int count)
{
ArraySegment<byte> segment = new ArraySegment<byte>(_data, _position, count);
_position += count;
return segment;
}
public ArraySegment<byte> GetRemainingBytesSegment()
{
ArraySegment<byte> segment = new ArraySegment<byte>(_data, _position, AvailableBytes);
_position = _data.Length;
return segment;
}
public T Get<T>() where T : struct, INetSerializable
{
var obj = default(T);
obj.Deserialize(this);
return obj;
}
public T Get<T>(Func<T> constructor) where T : class, INetSerializable
{
var obj = constructor();
obj.Deserialize(this);
return obj;
}
public byte[] GetRemainingBytes()
{
byte[] outgoingData = new byte[AvailableBytes];
Buffer.BlockCopy(_data, _position, outgoingData, 0, AvailableBytes);
_position = _data.Length;
return outgoingData;
}
public void GetBytes(byte[] destination, int start, int count)
{
Buffer.BlockCopy(_data, _position, destination, start, count);
_position += count;
}
public void GetBytes(byte[] destination, int count)
{
Buffer.BlockCopy(_data, _position, destination, 0, count);
_position += count;
}
public sbyte[] GetSBytesWithLength()
{
return GetArray<sbyte>(1);
}
public byte[] GetBytesWithLength()
{
return GetArray<byte>(1);
}
#endregion
#region PeekMethods
public byte PeekByte()
{
return _data[_position];
}
public sbyte PeekSByte()
{
return (sbyte)_data[_position];
}
public bool PeekBool()
{
return _data[_position] == 1;
}
public char PeekChar()
{
return (char)PeekUShort();
}
public ushort PeekUShort()
{
return BitConverter.ToUInt16(_data, _position);
}
public short PeekShort()
{
return BitConverter.ToInt16(_data, _position);
}
public long PeekLong()
{
return BitConverter.ToInt64(_data, _position);
}
public ulong PeekULong()
{
return BitConverter.ToUInt64(_data, _position);
}
public int PeekInt()
{
return BitConverter.ToInt32(_data, _position);
}
public uint PeekUInt()
{
return BitConverter.ToUInt32(_data, _position);
}
public float PeekFloat()
{
return BitConverter.ToSingle(_data, _position);
}
public double PeekDouble()
{
return BitConverter.ToDouble(_data, _position);
}
/// <summary>
/// Note that "maxLength" only limits the number of characters in a string, not its size in bytes.
/// </summary>
public string PeekString(int maxLength)
{
ushort size = PeekUShort();
if (size == 0)
{
return string.Empty;
}
int actualSize = size - 1;
if (actualSize >= NetDataWriter.StringBufferMaxLength)
{
return null;
}
return (maxLength > 0 && NetDataWriter.uTF8Encoding.Value.GetCharCount(_data, _position + 2, actualSize) > maxLength) ?
string.Empty :
NetDataWriter.uTF8Encoding.Value.GetString(_data, _position + 2, actualSize);
}
public string PeekString()
{
ushort size = PeekUShort();
if (size == 0)
{
return string.Empty;
}
int actualSize = size - 1;
if (actualSize >= NetDataWriter.StringBufferMaxLength)
{
return null;
}
return NetDataWriter.uTF8Encoding.Value.GetString(_data, _position + 2, actualSize);
}
#endregion
#region TryGetMethods
public bool TryGetByte(out byte result)
{
if (AvailableBytes >= 1)
{
result = GetByte();
return true;
}
result = 0;
return false;
}
public bool TryGetSByte(out sbyte result)
{
if (AvailableBytes >= 1)
{
result = GetSByte();
return true;
}
result = 0;
return false;
}
public bool TryGetBool(out bool result)
{
if (AvailableBytes >= 1)
{
result = GetBool();
return true;
}
result = false;
return false;
}
public bool TryGetChar(out char result)
{
if (!TryGetUShort(out ushort uShortValue))
{
result = '\0';
return false;
}
result = (char)uShortValue;
return true;
}
public bool TryGetShort(out short result)
{
if (AvailableBytes >= 2)
{
result = GetShort();
return true;
}
result = 0;
return false;
}
public bool TryGetUShort(out ushort result)
{
if (AvailableBytes >= 2)
{
result = GetUShort();
return true;
}
result = 0;
return false;
}
public bool TryGetInt(out int result)
{
if (AvailableBytes >= 4)
{
result = GetInt();
return true;
}
result = 0;
return false;
}
public bool TryGetUInt(out uint result)
{
if (AvailableBytes >= 4)
{
result = GetUInt();
return true;
}
result = 0;
return false;
}
public bool TryGetLong(out long result)
{
if (AvailableBytes >= 8)
{
result = GetLong();
return true;
}
result = 0;
return false;
}
public bool TryGetULong(out ulong result)
{
if (AvailableBytes >= 8)
{
result = GetULong();
return true;
}
result = 0;
return false;
}
public bool TryGetFloat(out float result)
{
if (AvailableBytes >= 4)
{
result = GetFloat();
return true;
}
result = 0;
return false;
}
public bool TryGetDouble(out double result)
{
if (AvailableBytes >= 8)
{
result = GetDouble();
return true;
}
result = 0;
return false;
}
public bool TryGetString(out string result)
{
if (AvailableBytes >= 2)
{
ushort strSize = PeekUShort();
if (AvailableBytes >= strSize + 1)
{
result = GetString();
return true;
}
}
result = null;
return false;
}
public bool TryGetStringArray(out string[] result)
{
if (!TryGetUShort(out ushort strArrayLength)) {
result = null;
return false;
}
result = new string[strArrayLength];
for (int i = 0; i < strArrayLength; i++)
{
if (!TryGetString(out result[i]))
{
result = null;
return false;
}
}
return true;
}
public bool TryGetBytesWithLength(out byte[] result)
{
if (AvailableBytes >= 2)
{
ushort length = PeekUShort();
if (length >= 0 && AvailableBytes >= 2 + length)
{
result = GetBytesWithLength();
return true;
}
}
result = null;
return false;
}
#endregion
public void Clear()
{
_position = 0;
_dataSize = 0;
_data = null;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 40f1663f22634e9499bb37bbe6a10992
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,381 @@
using System;
using System.Net;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
namespace LiteNetLib.Utils
{
public class NetDataWriter
{
protected byte[] _data;
protected int _position;
private const int InitialSize = 64;
private readonly bool _autoResize;
public int Capacity
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _data.Length;
}
public byte[] Data
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _data;
}
public int Length
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _position;
}
public static readonly ThreadLocal<UTF8Encoding> uTF8Encoding = new ThreadLocal<UTF8Encoding>(() => new UTF8Encoding(false, true));
public const int StringBufferMaxLength = 65535;
private readonly byte[] _stringBuffer = new byte[StringBufferMaxLength];
public NetDataWriter() : this(true, InitialSize)
{
}
public NetDataWriter(bool autoResize) : this(autoResize, InitialSize)
{
}
public NetDataWriter(bool autoResize, int initialSize)
{
_data = new byte[initialSize];
_autoResize = autoResize;
}
/// <summary>
/// Creates NetDataWriter from existing ByteArray
/// </summary>
/// <param name="bytes">Source byte array</param>
/// <param name="copy">Copy array to new location or use existing</param>
public static NetDataWriter FromBytes(byte[] bytes, bool copy)
{
if (copy)
{
var netDataWriter = new NetDataWriter(true, bytes.Length);
netDataWriter.Put(bytes);
return netDataWriter;
}
return new NetDataWriter(true, 0) {_data = bytes, _position = bytes.Length};
}
/// <summary>
/// Creates NetDataWriter from existing ByteArray (always copied data)
/// </summary>
/// <param name="bytes">Source byte array</param>
/// <param name="offset">Offset of array</param>
/// <param name="length">Length of array</param>
public static NetDataWriter FromBytes(byte[] bytes, int offset, int length)
{
var netDataWriter = new NetDataWriter(true, bytes.Length);
netDataWriter.Put(bytes, offset, length);
return netDataWriter;
}
public static NetDataWriter FromString(string value)
{
var netDataWriter = new NetDataWriter();
netDataWriter.Put(value);
return netDataWriter;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ResizeIfNeed(int newSize)
{
if (_data.Length < newSize)
{
Array.Resize(ref _data, Math.Max(newSize, _data.Length * 2));
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void EnsureFit(int additionalSize)
{
if (_data.Length < _position + additionalSize)
{
Array.Resize(ref _data, Math.Max(_position + additionalSize, _data.Length * 2));
}
}
public void Reset(int size)
{
ResizeIfNeed(size);
_position = 0;
}
public void Reset()
{
_position = 0;
}
public byte[] CopyData()
{
byte[] resultData = new byte[_position];
Buffer.BlockCopy(_data, 0, resultData, 0, _position);
return resultData;
}
/// <summary>
/// Sets position of NetDataWriter to rewrite previous values
/// </summary>
/// <param name="position">new byte position</param>
/// <returns>previous position of data writer</returns>
public int SetPosition(int position)
{
int prevPosition = _position;
_position = position;
return prevPosition;
}
public void Put(float value)
{
if (_autoResize)
ResizeIfNeed(_position + 4);
FastBitConverter.GetBytes(_data, _position, value);
_position += 4;
}
public void Put(double value)
{
if (_autoResize)
ResizeIfNeed(_position + 8);
FastBitConverter.GetBytes(_data, _position, value);
_position += 8;
}
public void Put(long value)
{
if (_autoResize)
ResizeIfNeed(_position + 8);
FastBitConverter.GetBytes(_data, _position, value);
_position += 8;
}
public void Put(ulong value)
{
if (_autoResize)
ResizeIfNeed(_position + 8);
FastBitConverter.GetBytes(_data, _position, value);
_position += 8;
}
public void Put(int value)
{
if (_autoResize)
ResizeIfNeed(_position + 4);
FastBitConverter.GetBytes(_data, _position, value);
_position += 4;
}
public void Put(uint value)
{
if (_autoResize)
ResizeIfNeed(_position + 4);
FastBitConverter.GetBytes(_data, _position, value);
_position += 4;
}
public void Put(char value)
{
Put((ushort)value);
}
public void Put(ushort value)
{
if (_autoResize)
ResizeIfNeed(_position + 2);
FastBitConverter.GetBytes(_data, _position, value);
_position += 2;
}
public void Put(short value)
{
if (_autoResize)
ResizeIfNeed(_position + 2);
FastBitConverter.GetBytes(_data, _position, value);
_position += 2;
}
public void Put(sbyte value)
{
if (_autoResize)
ResizeIfNeed(_position + 1);
_data[_position] = (byte)value;
_position++;
}
public void Put(byte value)
{
if (_autoResize)
ResizeIfNeed(_position + 1);
_data[_position] = value;
_position++;
}
public void Put(byte[] data, int offset, int length)
{
if (_autoResize)
ResizeIfNeed(_position + length);
Buffer.BlockCopy(data, offset, _data, _position, length);
_position += length;
}
public void Put(byte[] data)
{
if (_autoResize)
ResizeIfNeed(_position + data.Length);
Buffer.BlockCopy(data, 0, _data, _position, data.Length);
_position += data.Length;
}
public void PutSBytesWithLength(sbyte[] data, int offset, ushort length)
{
if (_autoResize)
ResizeIfNeed(_position + 2 + length);
FastBitConverter.GetBytes(_data, _position, length);
Buffer.BlockCopy(data, offset, _data, _position + 2, length);
_position += 2 + length;
}
public void PutSBytesWithLength(sbyte[] data)
{
PutArray(data, 1);
}
public void PutBytesWithLength(byte[] data, int offset, ushort length)
{
if (_autoResize)
ResizeIfNeed(_position + 2 + length);
FastBitConverter.GetBytes(_data, _position, length);
Buffer.BlockCopy(data, offset, _data, _position + 2, length);
_position += 2 + length;
}
public void PutBytesWithLength(byte[] data)
{
PutArray(data, 1);
}
public void Put(bool value)
{
Put((byte)(value ? 1 : 0));
}
public void PutArray(Array arr, int sz)
{
ushort length = arr == null ? (ushort) 0 : (ushort)arr.Length;
sz *= length;
if (_autoResize)
ResizeIfNeed(_position + sz + 2);
FastBitConverter.GetBytes(_data, _position, length);
if (arr != null)
Buffer.BlockCopy(arr, 0, _data, _position + 2, sz);
_position += sz + 2;
}
public void PutArray(float[] value)
{
PutArray(value, 4);
}
public void PutArray(double[] value)
{
PutArray(value, 8);
}
public void PutArray(long[] value)
{
PutArray(value, 8);
}
public void PutArray(ulong[] value)
{
PutArray(value, 8);
}
public void PutArray(int[] value)
{
PutArray(value, 4);
}
public void PutArray(uint[] value)
{
PutArray(value, 4);
}
public void PutArray(ushort[] value)
{
PutArray(value, 2);
}
public void PutArray(short[] value)
{
PutArray(value, 2);
}
public void PutArray(bool[] value)
{
PutArray(value, 1);
}
public void PutArray(string[] value)
{
ushort strArrayLength = value == null ? (ushort)0 : (ushort)value.Length;
Put(strArrayLength);
for (int i = 0; i < strArrayLength; i++)
Put(value[i]);
}
public void PutArray(string[] value, int strMaxLength)
{
ushort strArrayLength = value == null ? (ushort)0 : (ushort)value.Length;
Put(strArrayLength);
for (int i = 0; i < strArrayLength; i++)
Put(value[i], strMaxLength);
}
public void Put(IPEndPoint endPoint)
{
Put(endPoint.Address.ToString());
Put(endPoint.Port);
}
public void Put(string value)
{
Put(value, 0);
}
/// <summary>
/// Note that "maxLength" only limits the number of characters in a string, not its size in bytes.
/// </summary>
public void Put(string value, int maxLength)
{
if (string.IsNullOrEmpty(value))
{
Put((ushort)0);
return;
}
int length = maxLength > 0 && value.Length > maxLength ? maxLength : value.Length;
int size = uTF8Encoding.Value.GetBytes(value, 0, length, _stringBuffer, 0);
if (size == 0 || size >= StringBufferMaxLength)
{
Put((ushort)0);
return;
}
Put(checked((ushort)(size + 1)));
Put(_stringBuffer, 0, size);
}
public void Put<T>(T obj) where T : INetSerializable
{
obj.Serialize(this);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1a7c2666c073f0241ba672f218278a4d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,267 @@
using System;
using System.Collections.Generic;
namespace LiteNetLib.Utils
{
public class NetPacketProcessor
{
private static class HashCache<T>
{
public static readonly ulong Id;
//FNV-1 64 bit hash
static HashCache()
{
ulong hash = 14695981039346656037UL; //offset
string typeName = typeof(T).ToString();
for (var i = 0; i < typeName.Length; i++)
{
hash ^= typeName[i];
hash *= 1099511628211UL; //prime
}
Id = hash;
}
}
protected delegate void SubscribeDelegate(NetDataReader reader, object userData);
private readonly NetSerializer _netSerializer;
private readonly Dictionary<ulong, SubscribeDelegate> _callbacks = new Dictionary<ulong, SubscribeDelegate>();
public NetPacketProcessor()
{
_netSerializer = new NetSerializer();
}
public NetPacketProcessor(int maxStringLength)
{
_netSerializer = new NetSerializer(maxStringLength);
}
protected virtual ulong GetHash<T>()
{
return HashCache<T>.Id;
}
protected virtual SubscribeDelegate GetCallbackFromData(NetDataReader reader)
{
ulong hash = reader.GetULong();
if (!_callbacks.TryGetValue(hash, out var action))
{
throw new ParseException("Undefined packet in NetDataReader");
}
return action;
}
protected virtual void WriteHash<T>(NetDataWriter writer)
{
writer.Put(GetHash<T>());
}
/// <summary>
/// Register nested property type
/// </summary>
/// <typeparam name="T">INetSerializable structure</typeparam>
public void RegisterNestedType<T>() where T : struct, INetSerializable
{
_netSerializer.RegisterNestedType<T>();
}
/// <summary>
/// Register nested property type
/// </summary>
/// <param name="writeDelegate"></param>
/// <param name="readDelegate"></param>
public void RegisterNestedType<T>(Action<NetDataWriter, T> writeDelegate, Func<NetDataReader, T> readDelegate)
{
_netSerializer.RegisterNestedType<T>(writeDelegate, readDelegate);
}
/// <summary>
/// Register nested property type
/// </summary>
/// <typeparam name="T">INetSerializable class</typeparam>
public void RegisterNestedType<T>(Func<T> constructor) where T : class, INetSerializable
{
_netSerializer.RegisterNestedType(constructor);
}
/// <summary>
/// Reads all available data from NetDataReader and calls OnReceive delegates
/// </summary>
/// <param name="reader">NetDataReader with packets data</param>
public void ReadAllPackets(NetDataReader reader)
{
while (reader.AvailableBytes > 0)
ReadPacket(reader);
}
/// <summary>
/// Reads all available data from NetDataReader and calls OnReceive delegates
/// </summary>
/// <param name="reader">NetDataReader with packets data</param>
/// <param name="userData">Argument that passed to OnReceivedEvent</param>
/// <exception cref="ParseException">Malformed packet</exception>
public void ReadAllPackets(NetDataReader reader, object userData)
{
while (reader.AvailableBytes > 0)
ReadPacket(reader, userData);
}
/// <summary>
/// Reads one packet from NetDataReader and calls OnReceive delegate
/// </summary>
/// <param name="reader">NetDataReader with packet</param>
/// <exception cref="ParseException">Malformed packet</exception>
public void ReadPacket(NetDataReader reader)
{
ReadPacket(reader, null);
}
public void Write<T>(NetDataWriter writer, T packet) where T : class, new()
{
WriteHash<T>(writer);
_netSerializer.Serialize(writer, packet);
}
public void WriteNetSerializable<T>(NetDataWriter writer, ref T packet) where T : INetSerializable
{
WriteHash<T>(writer);
packet.Serialize(writer);
}
/// <summary>
/// Reads one packet from NetDataReader and calls OnReceive delegate
/// </summary>
/// <param name="reader">NetDataReader with packet</param>
/// <param name="userData">Argument that passed to OnReceivedEvent</param>
/// <exception cref="ParseException">Malformed packet</exception>
public void ReadPacket(NetDataReader reader, object userData)
{
GetCallbackFromData(reader)(reader, userData);
}
/// <summary>
/// Register and subscribe to packet receive event
/// </summary>
/// <param name="onReceive">event that will be called when packet deserialized with ReadPacket method</param>
/// <param name="packetConstructor">Method that constructs packet instead of slow Activator.CreateInstance</param>
/// <exception cref="InvalidTypeException"><typeparamref name="T"/>'s fields are not supported, or it has no fields</exception>
public void Subscribe<T>(Action<T> onReceive, Func<T> packetConstructor) where T : class, new()
{
_netSerializer.Register<T>();
_callbacks[GetHash<T>()] = (reader, userData) =>
{
var reference = packetConstructor();
_netSerializer.Deserialize(reader, reference);
onReceive(reference);
};
}
/// <summary>
/// Register and subscribe to packet receive event (with userData)
/// </summary>
/// <param name="onReceive">event that will be called when packet deserialized with ReadPacket method</param>
/// <param name="packetConstructor">Method that constructs packet instead of slow Activator.CreateInstance</param>
/// <exception cref="InvalidTypeException"><typeparamref name="T"/>'s fields are not supported, or it has no fields</exception>
public void Subscribe<T, TUserData>(Action<T, TUserData> onReceive, Func<T> packetConstructor) where T : class, new()
{
_netSerializer.Register<T>();
_callbacks[GetHash<T>()] = (reader, userData) =>
{
var reference = packetConstructor();
_netSerializer.Deserialize(reader, reference);
onReceive(reference, (TUserData)userData);
};
}
/// <summary>
/// Register and subscribe to packet receive event
/// This method will overwrite last received packet class on receive (less garbage)
/// </summary>
/// <param name="onReceive">event that will be called when packet deserialized with ReadPacket method</param>
/// <exception cref="InvalidTypeException"><typeparamref name="T"/>'s fields are not supported, or it has no fields</exception>
public void SubscribeReusable<T>(Action<T> onReceive) where T : class, new()
{
_netSerializer.Register<T>();
var reference = new T();
_callbacks[GetHash<T>()] = (reader, userData) =>
{
_netSerializer.Deserialize(reader, reference);
onReceive(reference);
};
}
/// <summary>
/// Register and subscribe to packet receive event
/// This method will overwrite last received packet class on receive (less garbage)
/// </summary>
/// <param name="onReceive">event that will be called when packet deserialized with ReadPacket method</param>
/// <exception cref="InvalidTypeException"><typeparamref name="T"/>'s fields are not supported, or it has no fields</exception>
public void SubscribeReusable<T, TUserData>(Action<T, TUserData> onReceive) where T : class, new()
{
_netSerializer.Register<T>();
var reference = new T();
_callbacks[GetHash<T>()] = (reader, userData) =>
{
_netSerializer.Deserialize(reader, reference);
onReceive(reference, (TUserData)userData);
};
}
public void SubscribeNetSerializable<T, TUserData>(
Action<T, TUserData> onReceive,
Func<T> packetConstructor) where T : INetSerializable
{
_callbacks[GetHash<T>()] = (reader, userData) =>
{
var pkt = packetConstructor();
pkt.Deserialize(reader);
onReceive(pkt, (TUserData)userData);
};
}
public void SubscribeNetSerializable<T>(
Action<T> onReceive,
Func<T> packetConstructor) where T : INetSerializable
{
_callbacks[GetHash<T>()] = (reader, userData) =>
{
var pkt = packetConstructor();
pkt.Deserialize(reader);
onReceive(pkt);
};
}
public void SubscribeNetSerializable<T, TUserData>(
Action<T, TUserData> onReceive) where T : INetSerializable, new()
{
var reference = new T();
_callbacks[GetHash<T>()] = (reader, userData) =>
{
reference.Deserialize(reader);
onReceive(reference, (TUserData)userData);
};
}
public void SubscribeNetSerializable<T>(
Action<T> onReceive) where T : INetSerializable, new()
{
var reference = new T();
_callbacks[GetHash<T>()] = (reader, userData) =>
{
reference.Deserialize(reader);
onReceive(reference);
};
}
/// <summary>
/// Remove any subscriptions by type
/// </summary>
/// <typeparam name="T">Packet type</typeparam>
/// <returns>true if remove is success</returns>
public bool RemoveSubscription<T>()
{
return _callbacks.Remove(GetHash<T>());
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 31df8ab0b770a5149a8ba538a6d16989
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,738 @@
using System;
using System.Reflection;
using System.Collections.Generic;
using System.Net;
using System.Runtime.Serialization;
namespace LiteNetLib.Utils
{
public class InvalidTypeException : ArgumentException
{
public InvalidTypeException(string message) : base(message) { }
}
public class ParseException : Exception
{
public ParseException(string message) : base(message) { }
}
public class NetSerializer
{
private enum CallType
{
Basic,
Array,
List
}
private abstract class FastCall<T>
{
public CallType Type;
public virtual void Init(MethodInfo getMethod, MethodInfo setMethod, CallType type) { Type = type; }
public abstract void Read(T inf, NetDataReader r);
public abstract void Write(T inf, NetDataWriter w);
public abstract void ReadArray(T inf, NetDataReader r);
public abstract void WriteArray(T inf, NetDataWriter w);
public abstract void ReadList(T inf, NetDataReader r);
public abstract void WriteList(T inf, NetDataWriter w);
}
private abstract class FastCallSpecific<TClass, TProperty> : FastCall<TClass>
{
protected Func<TClass, TProperty> Getter;
protected Action<TClass, TProperty> Setter;
protected Func<TClass, TProperty[]> GetterArr;
protected Action<TClass, TProperty[]> SetterArr;
protected Func<TClass, List<TProperty>> GetterList;
protected Action<TClass, List<TProperty>> SetterList;
public override void ReadArray(TClass inf, NetDataReader r) { throw new InvalidTypeException("Unsupported type: " + typeof(TProperty) + "[]"); }
public override void WriteArray(TClass inf, NetDataWriter w) { throw new InvalidTypeException("Unsupported type: " + typeof(TProperty) + "[]"); }
public override void ReadList(TClass inf, NetDataReader r) { throw new InvalidTypeException("Unsupported type: List<" + typeof(TProperty) + ">"); }
public override void WriteList(TClass inf, NetDataWriter w) { throw new InvalidTypeException("Unsupported type: List<" + typeof(TProperty) + ">"); }
protected TProperty[] ReadArrayHelper(TClass inf, NetDataReader r)
{
ushort count = r.GetUShort();
var arr = GetterArr(inf);
arr = arr == null || arr.Length != count ? new TProperty[count] : arr;
SetterArr(inf, arr);
return arr;
}
protected TProperty[] WriteArrayHelper(TClass inf, NetDataWriter w)
{
var arr = GetterArr(inf);
w.Put((ushort)arr.Length);
return arr;
}
protected List<TProperty> ReadListHelper(TClass inf, NetDataReader r, out int len)
{
len = r.GetUShort();
var list = GetterList(inf);
if (list == null)
{
list = new List<TProperty>(len);
SetterList(inf, list);
}
return list;
}
protected List<TProperty> WriteListHelper(TClass inf, NetDataWriter w, out int len)
{
var list = GetterList(inf);
if (list == null)
{
len = 0;
w.Put(0);
return null;
}
len = list.Count;
w.Put((ushort)len);
return list;
}
public override void Init(MethodInfo getMethod, MethodInfo setMethod, CallType type)
{
base.Init(getMethod, setMethod, type);
switch (type)
{
case CallType.Array:
GetterArr = (Func<TClass, TProperty[]>)Delegate.CreateDelegate(typeof(Func<TClass, TProperty[]>), getMethod);
SetterArr = (Action<TClass, TProperty[]>)Delegate.CreateDelegate(typeof(Action<TClass, TProperty[]>), setMethod);
break;
case CallType.List:
GetterList = (Func<TClass, List<TProperty>>)Delegate.CreateDelegate(typeof(Func<TClass, List<TProperty>>), getMethod);
SetterList = (Action<TClass, List<TProperty>>)Delegate.CreateDelegate(typeof(Action<TClass, List<TProperty>>), setMethod);
break;
default:
Getter = (Func<TClass, TProperty>)Delegate.CreateDelegate(typeof(Func<TClass, TProperty>), getMethod);
Setter = (Action<TClass, TProperty>)Delegate.CreateDelegate(typeof(Action<TClass, TProperty>), setMethod);
break;
}
}
}
private abstract class FastCallSpecificAuto<TClass, TProperty> : FastCallSpecific<TClass, TProperty>
{
protected abstract void ElementRead(NetDataReader r, out TProperty prop);
protected abstract void ElementWrite(NetDataWriter w, ref TProperty prop);
public override void Read(TClass inf, NetDataReader r)
{
ElementRead(r, out var elem);
Setter(inf, elem);
}
public override void Write(TClass inf, NetDataWriter w)
{
var elem = Getter(inf);
ElementWrite(w, ref elem);
}
public override void ReadArray(TClass inf, NetDataReader r)
{
var arr = ReadArrayHelper(inf, r);
for (int i = 0; i < arr.Length; i++)
ElementRead(r, out arr[i]);
}
public override void WriteArray(TClass inf, NetDataWriter w)
{
var arr = WriteArrayHelper(inf, w);
for (int i = 0; i < arr.Length; i++)
ElementWrite(w, ref arr[i]);
}
}
private sealed class FastCallStatic<TClass, TProperty> : FastCallSpecific<TClass, TProperty>
{
private readonly Action<NetDataWriter, TProperty> _writer;
private readonly Func<NetDataReader, TProperty> _reader;
public FastCallStatic(Action<NetDataWriter, TProperty> write, Func<NetDataReader, TProperty> read)
{
_writer = write;
_reader = read;
}
public override void Read(TClass inf, NetDataReader r) { Setter(inf, _reader(r)); }
public override void Write(TClass inf, NetDataWriter w) { _writer(w, Getter(inf)); }
public override void ReadList(TClass inf, NetDataReader r)
{
var list = ReadListHelper(inf, r, out int len);
int listCount = list.Count;
for (int i = 0; i < len; i++)
{
if (i < listCount)
list[i] = _reader(r);
else
list.Add(_reader(r));
}
if (len < listCount)
list.RemoveRange(len, listCount - len);
}
public override void WriteList(TClass inf, NetDataWriter w)
{
var list = WriteListHelper(inf, w, out int len);
for (int i = 0; i < len; i++)
_writer(w, list[i]);
}
public override void ReadArray(TClass inf, NetDataReader r)
{
var arr = ReadArrayHelper(inf, r);
int len = arr.Length;
for (int i = 0; i < len; i++)
arr[i] = _reader(r);
}
public override void WriteArray(TClass inf, NetDataWriter w)
{
var arr = WriteArrayHelper(inf, w);
int len = arr.Length;
for (int i = 0; i < len; i++)
_writer(w, arr[i]);
}
}
private sealed class FastCallStruct<TClass, TProperty> : FastCallSpecific<TClass, TProperty> where TProperty : struct, INetSerializable
{
private TProperty _p;
public override void Read(TClass inf, NetDataReader r)
{
_p.Deserialize(r);
Setter(inf, _p);
}
public override void Write(TClass inf, NetDataWriter w)
{
_p = Getter(inf);
_p.Serialize(w);
}
public override void ReadList(TClass inf, NetDataReader r)
{
var list = ReadListHelper(inf, r, out int len);
int listCount = list.Count;
for (int i = 0; i < len; i++)
{
var itm = default(TProperty);
itm.Deserialize(r);
if(i < listCount)
list[i] = itm;
else
list.Add(itm);
}
if (len < listCount)
list.RemoveRange(len, listCount - len);
}
public override void WriteList(TClass inf, NetDataWriter w)
{
var list = WriteListHelper(inf, w, out int len);
for (int i = 0; i < len; i++)
list[i].Serialize(w);
}
public override void ReadArray(TClass inf, NetDataReader r)
{
var arr = ReadArrayHelper(inf, r);
int len = arr.Length;
for (int i = 0; i < len; i++)
arr[i].Deserialize(r);
}
public override void WriteArray(TClass inf, NetDataWriter w)
{
var arr = WriteArrayHelper(inf, w);
int len = arr.Length;
for (int i = 0; i < len; i++)
arr[i].Serialize(w);
}
}
private sealed class FastCallClass<TClass, TProperty> : FastCallSpecific<TClass, TProperty> where TProperty : class, INetSerializable
{
private readonly Func<TProperty> _constructor;
public FastCallClass(Func<TProperty> constructor) { _constructor = constructor; }
public override void Read(TClass inf, NetDataReader r)
{
var p = _constructor();
p.Deserialize(r);
Setter(inf, p);
}
public override void Write(TClass inf, NetDataWriter w)
{
var p = Getter(inf);
p?.Serialize(w);
}
public override void ReadList(TClass inf, NetDataReader r)
{
var list = ReadListHelper(inf, r, out int len);
int listCount = list.Count;
for (int i = 0; i < len; i++)
{
if (i < listCount)
{
list[i].Deserialize(r);
}
else
{
var itm = _constructor();
itm.Deserialize(r);
list.Add(itm);
}
}
if (len < listCount)
list.RemoveRange(len, listCount - len);
}
public override void WriteList(TClass inf, NetDataWriter w)
{
var list = WriteListHelper(inf, w, out int len);
for (int i = 0; i < len; i++)
list[i].Serialize(w);
}
public override void ReadArray(TClass inf, NetDataReader r)
{
var arr = ReadArrayHelper(inf, r);
int len = arr.Length;
for (int i = 0; i < len; i++)
{
arr[i] = _constructor();
arr[i].Deserialize(r);
}
}
public override void WriteArray(TClass inf, NetDataWriter w)
{
var arr = WriteArrayHelper(inf, w);
int len = arr.Length;
for (int i = 0; i < len; i++)
arr[i].Serialize(w);
}
}
private class IntSerializer<T> : FastCallSpecific<T, int>
{
public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetInt()); }
public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); }
public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetIntArray()); }
public override void WriteArray(T inf, NetDataWriter w) { w.PutArray(GetterArr(inf)); }
}
private class UIntSerializer<T> : FastCallSpecific<T, uint>
{
public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetUInt()); }
public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); }
public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetUIntArray()); }
public override void WriteArray(T inf, NetDataWriter w) { w.PutArray(GetterArr(inf)); }
}
private class ShortSerializer<T> : FastCallSpecific<T, short>
{
public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetShort()); }
public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); }
public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetShortArray()); }
public override void WriteArray(T inf, NetDataWriter w) { w.PutArray(GetterArr(inf)); }
}
private class UShortSerializer<T> : FastCallSpecific<T, ushort>
{
public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetUShort()); }
public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); }
public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetUShortArray()); }
public override void WriteArray(T inf, NetDataWriter w) { w.PutArray(GetterArr(inf)); }
}
private class LongSerializer<T> : FastCallSpecific<T, long>
{
public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetLong()); }
public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); }
public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetLongArray()); }
public override void WriteArray(T inf, NetDataWriter w) { w.PutArray(GetterArr(inf)); }
}
private class ULongSerializer<T> : FastCallSpecific<T, ulong>
{
public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetULong()); }
public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); }
public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetULongArray()); }
public override void WriteArray(T inf, NetDataWriter w) { w.PutArray(GetterArr(inf)); }
}
private class ByteSerializer<T> : FastCallSpecific<T, byte>
{
public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetByte()); }
public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); }
public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetBytesWithLength()); }
public override void WriteArray(T inf, NetDataWriter w) { w.PutBytesWithLength(GetterArr(inf)); }
}
private class SByteSerializer<T> : FastCallSpecific<T, sbyte>
{
public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetSByte()); }
public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); }
public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetSBytesWithLength()); }
public override void WriteArray(T inf, NetDataWriter w) { w.PutSBytesWithLength(GetterArr(inf)); }
}
private class FloatSerializer<T> : FastCallSpecific<T, float>
{
public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetFloat()); }
public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); }
public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetFloatArray()); }
public override void WriteArray(T inf, NetDataWriter w) { w.PutArray(GetterArr(inf)); }
}
private class DoubleSerializer<T> : FastCallSpecific<T, double>
{
public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetDouble()); }
public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); }
public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetDoubleArray()); }
public override void WriteArray(T inf, NetDataWriter w) { w.PutArray(GetterArr(inf)); }
}
private class BoolSerializer<T> : FastCallSpecific<T, bool>
{
public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetBool()); }
public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); }
public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetBoolArray()); }
public override void WriteArray(T inf, NetDataWriter w) { w.PutArray(GetterArr(inf)); }
}
private class CharSerializer<T> : FastCallSpecificAuto<T, char>
{
protected override void ElementWrite(NetDataWriter w, ref char prop) { w.Put(prop); }
protected override void ElementRead(NetDataReader r, out char prop) { prop = r.GetChar(); }
}
private class IPEndPointSerializer<T> : FastCallSpecificAuto<T, IPEndPoint>
{
protected override void ElementWrite(NetDataWriter w, ref IPEndPoint prop) { w.Put(prop); }
protected override void ElementRead(NetDataReader r, out IPEndPoint prop) { prop = r.GetNetEndPoint(); }
}
private class StringSerializer<T> : FastCallSpecific<T, string>
{
private readonly int _maxLength;
public StringSerializer(int maxLength) { _maxLength = maxLength > 0 ? maxLength : short.MaxValue; }
public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetString(_maxLength)); }
public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf), _maxLength); }
public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetStringArray(_maxLength)); }
public override void WriteArray(T inf, NetDataWriter w) { w.PutArray(GetterArr(inf), _maxLength); }
}
private class EnumByteSerializer<T> : FastCall<T>
{
protected readonly PropertyInfo Property;
protected readonly Type PropertyType;
public EnumByteSerializer(PropertyInfo property, Type propertyType)
{
Property = property;
PropertyType = propertyType;
}
public override void Read(T inf, NetDataReader r) { Property.SetValue(inf, Enum.ToObject(PropertyType, r.GetByte()), null); }
public override void Write(T inf, NetDataWriter w) { w.Put((byte)Property.GetValue(inf, null)); }
public override void ReadArray(T inf, NetDataReader r) { throw new InvalidTypeException("Unsupported type: Enum[]"); }
public override void WriteArray(T inf, NetDataWriter w) { throw new InvalidTypeException("Unsupported type: Enum[]"); }
public override void ReadList(T inf, NetDataReader r) { throw new InvalidTypeException("Unsupported type: List<Enum>"); }
public override void WriteList(T inf, NetDataWriter w) { throw new InvalidTypeException("Unsupported type: List<Enum>"); }
}
private class EnumIntSerializer<T> : EnumByteSerializer<T>
{
public EnumIntSerializer(PropertyInfo property, Type propertyType) : base(property, propertyType) { }
public override void Read(T inf, NetDataReader r) { Property.SetValue(inf, Enum.ToObject(PropertyType, r.GetInt()), null); }
public override void Write(T inf, NetDataWriter w) { w.Put((int)Property.GetValue(inf, null)); }
}
private sealed class ClassInfo<T>
{
public static ClassInfo<T> Instance;
private readonly FastCall<T>[] _serializers;
private readonly int _membersCount;
public ClassInfo(List<FastCall<T>> serializers)
{
_membersCount = serializers.Count;
_serializers = serializers.ToArray();
}
public void Write(T obj, NetDataWriter writer)
{
for (int i = 0; i < _membersCount; i++)
{
var s = _serializers[i];
if (s.Type == CallType.Basic)
s.Write(obj, writer);
else if (s.Type == CallType.Array)
s.WriteArray(obj, writer);
else
s.WriteList(obj, writer);
}
}
public void Read(T obj, NetDataReader reader)
{
for (int i = 0; i < _membersCount; i++)
{
var s = _serializers[i];
if (s.Type == CallType.Basic)
s.Read(obj, reader);
else if(s.Type == CallType.Array)
s.ReadArray(obj, reader);
else
s.ReadList(obj, reader);
}
}
}
private abstract class CustomType
{
public abstract FastCall<T> Get<T>();
}
private sealed class CustomTypeStruct<TProperty> : CustomType where TProperty : struct, INetSerializable
{
public override FastCall<T> Get<T>() { return new FastCallStruct<T, TProperty>(); }
}
private sealed class CustomTypeClass<TProperty> : CustomType where TProperty : class, INetSerializable
{
private readonly Func<TProperty> _constructor;
public CustomTypeClass(Func<TProperty> constructor) { _constructor = constructor; }
public override FastCall<T> Get<T>() { return new FastCallClass<T, TProperty>(_constructor); }
}
private sealed class CustomTypeStatic<TProperty> : CustomType
{
private readonly Action<NetDataWriter, TProperty> _writer;
private readonly Func<NetDataReader, TProperty> _reader;
public CustomTypeStatic(Action<NetDataWriter, TProperty> writer, Func<NetDataReader, TProperty> reader)
{
_writer = writer;
_reader = reader;
}
public override FastCall<T> Get<T>() { return new FastCallStatic<T, TProperty>(_writer, _reader); }
}
/// <summary>
/// Register custom property type
/// </summary>
/// <typeparam name="T">INetSerializable structure</typeparam>
public void RegisterNestedType<T>() where T : struct, INetSerializable
{
_registeredTypes.Add(typeof(T), new CustomTypeStruct<T>());
}
/// <summary>
/// Register custom property type
/// </summary>
/// <typeparam name="T">INetSerializable class</typeparam>
public void RegisterNestedType<T>(Func<T> constructor) where T : class, INetSerializable
{
_registeredTypes.Add(typeof(T), new CustomTypeClass<T>(constructor));
}
/// <summary>
/// Register custom property type
/// </summary>
/// <typeparam name="T">Any packet</typeparam>
/// <param name="writer">custom type writer</param>
/// <param name="reader">custom type reader</param>
public void RegisterNestedType<T>(Action<NetDataWriter, T> writer, Func<NetDataReader, T> reader)
{
_registeredTypes.Add(typeof(T), new CustomTypeStatic<T>(writer, reader));
}
private NetDataWriter _writer;
private readonly int _maxStringLength;
private readonly Dictionary<Type, CustomType> _registeredTypes = new Dictionary<Type, CustomType>();
public NetSerializer() : this(0)
{
}
public NetSerializer(int maxStringLength)
{
_maxStringLength = maxStringLength;
}
private ClassInfo<T> RegisterInternal<T>()
{
if (ClassInfo<T>.Instance != null)
return ClassInfo<T>.Instance;
Type t = typeof(T);
var props = t.GetProperties(
BindingFlags.Instance |
BindingFlags.Public |
BindingFlags.GetProperty |
BindingFlags.SetProperty);
var serializers = new List<FastCall<T>>();
for (int i = 0; i < props.Length; i++)
{
var property = props[i];
var propertyType = property.PropertyType;
var elementType = propertyType.IsArray ? propertyType.GetElementType() : propertyType;
var callType = propertyType.IsArray ? CallType.Array : CallType.Basic;
if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(List<>))
{
elementType = propertyType.GetGenericArguments()[0];
callType = CallType.List;
}
if (Attribute.IsDefined(property, typeof(IgnoreDataMemberAttribute)))
continue;
var getMethod = property.GetGetMethod();
var setMethod = property.GetSetMethod();
if (getMethod == null || setMethod == null)
continue;
FastCall<T> serialzer = null;
if (propertyType.IsEnum)
{
var underlyingType = Enum.GetUnderlyingType(propertyType);
if (underlyingType == typeof(byte))
serialzer = new EnumByteSerializer<T>(property, propertyType);
else if (underlyingType == typeof(int))
serialzer = new EnumIntSerializer<T>(property, propertyType);
else
throw new InvalidTypeException("Not supported enum underlying type: " + underlyingType.Name);
}
else if (elementType == typeof(string))
serialzer = new StringSerializer<T>(_maxStringLength);
else if (elementType == typeof(bool))
serialzer = new BoolSerializer<T>();
else if (elementType == typeof(byte))
serialzer = new ByteSerializer<T>();
else if (elementType == typeof(sbyte))
serialzer = new SByteSerializer<T>();
else if (elementType == typeof(short))
serialzer = new ShortSerializer<T>();
else if (elementType == typeof(ushort))
serialzer = new UShortSerializer<T>();
else if (elementType == typeof(int))
serialzer = new IntSerializer<T>();
else if (elementType == typeof(uint))
serialzer = new UIntSerializer<T>();
else if (elementType == typeof(long))
serialzer = new LongSerializer<T>();
else if (elementType == typeof(ulong))
serialzer = new ULongSerializer<T>();
else if (elementType == typeof(float))
serialzer = new FloatSerializer<T>();
else if (elementType == typeof(double))
serialzer = new DoubleSerializer<T>();
else if (elementType == typeof(char))
serialzer = new CharSerializer<T>();
else if (elementType == typeof(IPEndPoint))
serialzer = new IPEndPointSerializer<T>();
else
{
_registeredTypes.TryGetValue(elementType, out var customType);
if (customType != null)
serialzer = customType.Get<T>();
}
if (serialzer != null)
{
serialzer.Init(getMethod, setMethod, callType);
serializers.Add(serialzer);
}
else
{
throw new InvalidTypeException("Unknown property type: " + propertyType.FullName);
}
}
ClassInfo<T>.Instance = new ClassInfo<T>(serializers);
return ClassInfo<T>.Instance;
}
/// <exception cref="InvalidTypeException"><typeparamref name="T"/>'s fields are not supported, or it has no fields</exception>
public void Register<T>()
{
RegisterInternal<T>();
}
/// <summary>
/// Reads packet with known type
/// </summary>
/// <param name="reader">NetDataReader with packet</param>
/// <returns>Returns packet if packet in reader is matched type</returns>
/// <exception cref="InvalidTypeException"><typeparamref name="T"/>'s fields are not supported, or it has no fields</exception>
public T Deserialize<T>(NetDataReader reader) where T : class, new()
{
var info = RegisterInternal<T>();
var result = new T();
try
{
info.Read(result, reader);
}
catch
{
return null;
}
return result;
}
/// <summary>
/// Reads packet with known type (non alloc variant)
/// </summary>
/// <param name="reader">NetDataReader with packet</param>
/// <param name="target">Deserialization target</param>
/// <returns>Returns true if packet in reader is matched type</returns>
/// <exception cref="InvalidTypeException"><typeparamref name="T"/>'s fields are not supported, or it has no fields</exception>
public bool Deserialize<T>(NetDataReader reader, T target) where T : class, new()
{
var info = RegisterInternal<T>();
try
{
info.Read(target, reader);
}
catch
{
return false;
}
return true;
}
/// <summary>
/// Serialize object to NetDataWriter (fast)
/// </summary>
/// <param name="writer">Serialization target NetDataWriter</param>
/// <param name="obj">Object to serialize</param>
/// <exception cref="InvalidTypeException"><typeparamref name="T"/>'s fields are not supported, or it has no fields</exception>
public void Serialize<T>(NetDataWriter writer, T obj) where T : class, new()
{
RegisterInternal<T>().Write(obj, writer);
}
/// <summary>
/// Serialize object to byte array
/// </summary>
/// <param name="obj">Object to serialize</param>
/// <returns>byte array with serialized data</returns>
public byte[] Serialize<T>(T obj) where T : class, new()
{
if (_writer == null)
_writer = new NetDataWriter();
_writer.Reset();
Serialize(_writer, obj);
return _writer.CopyData();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e77693e1445e6a748b5044f29adc4d4e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,423 @@
using System;
namespace LiteNetLib.Utils
{
/// <summary>
/// Represents RFC4330 SNTP packet used for communication to and from a network time server.
/// </summary>
/// <remarks>
/// <para>
/// Most applications should just use the <see cref="NtpPacket.CorrectionOffset" /> property.
/// </para>
/// <para>
/// The same data structure represents both request and reply packets.
/// Request and reply differ in which properties are set and to what values.
/// </para>
/// <para>
/// The only real property is <see cref="NtpPacket.Bytes" />.
/// All other properties read from and write to the underlying byte array
/// with the exception of <see cref="NtpPacket.DestinationTimestamp" />,
/// which is not part of the packet on network and it is instead set locally after receiving the packet.
/// </para>
/// <para>
/// Copied from <a href="https://guerrillantp.machinezoo.com/">GuerrillaNtp project</a>
/// with permission from Robert Vazan (@robertvazan) under MIT license, see https://github.com/RevenantX/LiteNetLib/pull/236
/// </para>
/// </remarks>
public class NtpPacket
{
private static readonly DateTime Epoch = new DateTime(1900, 1, 1);
/// <summary>
/// Gets RFC4330-encoded SNTP packet.
/// </summary>
/// <value>
/// Byte array containing RFC4330-encoded SNTP packet. It is at least 48 bytes long.
/// </value>
/// <remarks>
/// This is the only real property. All other properties except
/// <see cref="NtpPacket.DestinationTimestamp" /> read from or write to this byte array.
/// </remarks>
public byte[] Bytes { get; }
/// <summary>
/// Gets the leap second indicator.
/// </summary>
/// <value>
/// Leap second warning, if any. Special value
/// <see cref="NtpLeapIndicator.AlarmCondition" /> indicates unsynchronized server clock.
/// Default is <see cref="NtpLeapIndicator.NoWarning" />.
/// </value>
/// <remarks>
/// Only servers fill in this property. Clients can consult this property for possible leap second warning.
/// </remarks>
public NtpLeapIndicator LeapIndicator => (NtpLeapIndicator)((Bytes[0] & 0xC0) >> 6);
/// <summary>
/// Gets or sets protocol version number.
/// </summary>
/// <value>
/// SNTP protocol version. Default is 4, which is the latest version at the time of this writing.
/// </value>
/// <remarks>
/// In request packets, clients should leave this property at default value 4.
/// Servers usually reply with the same protocol version.
/// </remarks>
public int VersionNumber
{
get => (Bytes[0] & 0x38) >> 3;
private set => Bytes[0] = (byte)((Bytes[0] & ~0x38) | value << 3);
}
/// <summary>
/// Gets or sets SNTP packet mode, i.e. whether this is client or server packet.
/// </summary>
/// <value>
/// SNTP packet mode. Default is <see cref="NtpMode.Client" /> in newly created packets.
/// Server reply should have this property set to <see cref="NtpMode.Server" />.
/// </value>
public NtpMode Mode
{
get => (NtpMode)(Bytes[0] & 0x07);
private set => Bytes[0] = (byte)((Bytes[0] & ~0x07) | (int)value);
}
/// <summary>
/// Gets server's distance from the reference clock.
/// </summary>
/// <value>
/// <para>
/// Distance from the reference clock. This property is set only in server reply packets.
/// Servers connected directly to reference clock hardware set this property to 1.
/// Statum number is incremented by 1 on every hop down the NTP server hierarchy.
/// </para>
/// <para>
/// Special value 0 indicates that this packet is a Kiss-o'-Death message
/// with kiss code stored in <see cref="NtpPacket.ReferenceId" />.
/// </para>
/// </value>
public int Stratum => Bytes[1];
/// <summary>
/// Gets server's preferred polling interval.
/// </summary>
/// <value>
/// Polling interval in log2 seconds, e.g. 4 stands for 16s and 17 means 131,072s.
/// </value>
public int Poll => Bytes[2];
/// <summary>
/// Gets the precision of server clock.
/// </summary>
/// <value>
/// Clock precision in log2 seconds, e.g. -20 for microsecond precision.
/// </value>
public int Precision => (sbyte)Bytes[3];
/// <summary>
/// Gets the total round-trip delay from the server to the reference clock.
/// </summary>
/// <value>
/// Round-trip delay to the reference clock. Normally a positive value smaller than one second.
/// </value>
public TimeSpan RootDelay => GetTimeSpan32(4);
/// <summary>
/// Gets the estimated error in time reported by the server.
/// </summary>
/// <value>
/// Estimated error in time reported by the server. Normally a positive value smaller than one second.
/// </value>
public TimeSpan RootDispersion => GetTimeSpan32(8);
/// <summary>
/// Gets the ID of the time source used by the server or Kiss-o'-Death code sent by the server.
/// </summary>
/// <value>
/// <para>
/// ID of server's time source or Kiss-o'-Death code.
/// Purpose of this property depends on value of <see cref="NtpPacket.Stratum" /> property.
/// </para>
/// <para>
/// Stratum 1 servers write here one of several special values that describe the kind of hardware clock they use.
/// </para>
/// <para>
/// Stratum 2 and lower servers set this property to IPv4 address of their upstream server.
/// If upstream server has IPv6 address, the address is hashed, because it doesn't fit in this property.
/// </para>
/// <para>
/// When server sets <see cref="NtpPacket.Stratum" /> to special value 0,
/// this property contains so called kiss code that instructs the client to stop querying the server.
/// </para>
/// </value>
public uint ReferenceId => GetUInt32BE(12);
/// <summary>
/// Gets or sets the time when the server clock was last set or corrected.
/// </summary>
/// <value>
/// Time when the server clock was last set or corrected or <c>null</c> when not specified.
/// </value>
/// <remarks>
/// This Property is usually set only by servers. It usually lags server's current time by several minutes,
/// so don't use this property for time synchronization.
/// </remarks>
public DateTime? ReferenceTimestamp => GetDateTime64(16);
/// <summary>
/// Gets or sets the time when the client sent its request.
/// </summary>
/// <value>
/// This property is <c>null</c> in request packets.
/// In reply packets, it is the time when the client sent its request.
/// Servers copy this value from <see cref="NtpPacket.TransmitTimestamp" />
/// that they find in received request packet.
/// </value>
/// <seealso cref="NtpPacket.CorrectionOffset" />
/// <seealso cref="NtpPacket.RoundTripTime" />
public DateTime? OriginTimestamp => GetDateTime64(24);
/// <summary>
/// Gets or sets the time when the request was received by the server.
/// </summary>
/// <value>
/// This property is <c>null</c> in request packets.
/// In reply packets, it is the time when the server received client request.
/// </value>
/// <seealso cref="NtpPacket.CorrectionOffset" />
/// <seealso cref="NtpPacket.RoundTripTime" />
public DateTime? ReceiveTimestamp => GetDateTime64(32);
/// <summary>
/// Gets or sets the time when the packet was sent.
/// </summary>
/// <value>
/// Time when the packet was sent. It should never be <c>null</c>.
/// Default value is <see cref="System.DateTime.UtcNow" />.
/// </value>
/// <remarks>
/// This property must be set by both clients and servers.
/// </remarks>
/// <seealso cref="NtpPacket.CorrectionOffset" />
/// <seealso cref="NtpPacket.RoundTripTime" />
public DateTime? TransmitTimestamp { get { return GetDateTime64(40); } private set { SetDateTime64(40, value); } }
/// <summary>
/// Gets or sets the time of reception of response SNTP packet on the client.
/// </summary>
/// <value>
/// Time of reception of response SNTP packet on the client. It is <c>null</c> in request packets.
/// </value>
/// <remarks>
/// This property is not part of the protocol and has to be set when reply packet is received.
/// </remarks>
/// <seealso cref="NtpPacket.CorrectionOffset" />
/// <seealso cref="NtpPacket.RoundTripTime" />
public DateTime? DestinationTimestamp { get; private set; }
/// <summary>
/// Gets the round-trip time to the server.
/// </summary>
/// <value>
/// Time the request spent traveling to the server plus the time the reply spent traveling back.
/// This is calculated from timestamps in the packet as <c>(t1 - t0) + (t3 - t2)</c>
/// where t0 is <see cref="NtpPacket.OriginTimestamp" />,
/// t1 is <see cref="NtpPacket.ReceiveTimestamp" />,
/// t2 is <see cref="NtpPacket.TransmitTimestamp" />,
/// and t3 is <see cref="NtpPacket.DestinationTimestamp" />.
/// This property throws an exception in request packets.
/// </value>
public TimeSpan RoundTripTime
{
get
{
CheckTimestamps();
return (ReceiveTimestamp.Value - OriginTimestamp.Value) + (DestinationTimestamp.Value - TransmitTimestamp.Value);
}
}
/// <summary>
/// Gets the offset that should be added to local time to synchronize it with server time.
/// </summary>
/// <value>
/// Time difference between server and client. It should be added to local time to get server time.
/// It is calculated from timestamps in the packet as <c>0.5 * ((t1 - t0) - (t3 - t2))</c>
/// where t0 is <see cref="NtpPacket.OriginTimestamp" />,
/// t1 is <see cref="NtpPacket.ReceiveTimestamp" />,
/// t2 is <see cref="NtpPacket.TransmitTimestamp" />,
/// and t3 is <see cref="NtpPacket.DestinationTimestamp" />.
/// This property throws an exception in request packets.
/// </value>
public TimeSpan CorrectionOffset
{
get
{
CheckTimestamps();
return TimeSpan.FromTicks(((ReceiveTimestamp.Value - OriginTimestamp.Value) - (DestinationTimestamp.Value - TransmitTimestamp.Value)).Ticks / 2);
}
}
/// <summary>
/// Initializes default request packet.
/// </summary>
/// <remarks>
/// Properties <see cref="NtpPacket.Mode" /> and <see cref="NtpPacket.VersionNumber" />
/// are set appropriately for request packet. Property <see cref="NtpPacket.TransmitTimestamp" />
/// is set to <see cref="System.DateTime.UtcNow" />.
/// </remarks>
public NtpPacket() : this(new byte[48])
{
Mode = NtpMode.Client;
VersionNumber = 4;
TransmitTimestamp = DateTime.UtcNow;
}
/// <summary>
/// Initializes packet from received data.
/// </summary>
internal NtpPacket(byte[] bytes)
{
if (bytes.Length < 48)
throw new ArgumentException("SNTP reply packet must be at least 48 bytes long.", "bytes");
Bytes = bytes;
}
/// <summary>
/// Initializes packet from data received from a server.
/// </summary>
/// <param name="bytes">Data received from the server.</param>
/// <param name="destinationTimestamp">Utc time of reception of response SNTP packet on the client.</param>
/// <returns></returns>
public static NtpPacket FromServerResponse(byte[] bytes, DateTime destinationTimestamp)
{
return new NtpPacket(bytes) { DestinationTimestamp = destinationTimestamp };
}
internal void ValidateRequest()
{
if (Mode != NtpMode.Client)
throw new InvalidOperationException("This is not a request SNTP packet.");
if (VersionNumber == 0)
throw new InvalidOperationException("Protocol version of the request is not specified.");
if (TransmitTimestamp == null)
throw new InvalidOperationException("TransmitTimestamp must be set in request packet.");
}
internal void ValidateReply()
{
if (Mode != NtpMode.Server)
throw new InvalidOperationException("This is not a reply SNTP packet.");
if (VersionNumber == 0)
throw new InvalidOperationException("Protocol version of the reply is not specified.");
if (Stratum == 0)
throw new InvalidOperationException(string.Format("Received Kiss-o'-Death SNTP packet with code 0x{0:x}.", ReferenceId));
if (LeapIndicator == NtpLeapIndicator.AlarmCondition)
throw new InvalidOperationException("SNTP server has unsynchronized clock.");
CheckTimestamps();
}
private void CheckTimestamps()
{
if (OriginTimestamp == null)
throw new InvalidOperationException("Origin timestamp is missing.");
if (ReceiveTimestamp == null)
throw new InvalidOperationException("Receive timestamp is missing.");
if (TransmitTimestamp == null)
throw new InvalidOperationException("Transmit timestamp is missing.");
if (DestinationTimestamp == null)
throw new InvalidOperationException("Destination timestamp is missing.");
}
private DateTime? GetDateTime64(int offset)
{
var field = GetUInt64BE(offset);
if (field == 0)
return null;
return new DateTime(Epoch.Ticks + Convert.ToInt64(field * (1.0 / (1L << 32) * 10000000.0)));
}
private void SetDateTime64(int offset, DateTime? value)
{
SetUInt64BE(offset, value == null ? 0 : Convert.ToUInt64((value.Value.Ticks - Epoch.Ticks) * (0.0000001 * (1L << 32))));
}
private TimeSpan GetTimeSpan32(int offset)
{
return TimeSpan.FromSeconds(GetInt32BE(offset) / (double)(1 << 16));
}
private ulong GetUInt64BE(int offset)
{
return SwapEndianness(BitConverter.ToUInt64(Bytes, offset));
}
private void SetUInt64BE(int offset, ulong value)
{
FastBitConverter.GetBytes(Bytes, offset, SwapEndianness(value));
}
private int GetInt32BE(int offset)
{
return (int)GetUInt32BE(offset);
}
private uint GetUInt32BE(int offset)
{
return SwapEndianness(BitConverter.ToUInt32(Bytes, offset));
}
private static uint SwapEndianness(uint x)
{
return ((x & 0xff) << 24) | ((x & 0xff00) << 8) | ((x & 0xff0000) >> 8) | ((x & 0xff000000) >> 24);
}
private static ulong SwapEndianness(ulong x)
{
return ((ulong)SwapEndianness((uint)x) << 32) | SwapEndianness((uint)(x >> 32));
}
}
/// <summary>
/// Represents leap second warning from the server that instructs the client to add or remove leap second.
/// </summary>
/// <seealso cref="NtpPacket.LeapIndicator" />
public enum NtpLeapIndicator
{
/// <summary>
/// No leap second warning. No action required.
/// </summary>
NoWarning,
/// <summary>
/// Warns the client that the last minute of the current day has 61 seconds.
/// </summary>
LastMinuteHas61Seconds,
/// <summary>
/// Warns the client that the last minute of the current day has 59 seconds.
/// </summary>
LastMinuteHas59Seconds,
/// <summary>
/// Special value indicating that the server clock is unsynchronized and the returned time is unreliable.
/// </summary>
AlarmCondition
}
/// <summary>
/// Describes SNTP packet mode, i.e. client or server.
/// </summary>
/// <seealso cref="NtpPacket.Mode" />
public enum NtpMode
{
/// <summary>
/// Identifies client-to-server SNTP packet.
/// </summary>
Client = 3,
/// <summary>
/// Identifies server-to-client SNTP packet.
/// </summary>
Server = 4,
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a1ab93956916692409ea5f00f581db6f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,42 @@
using System.Net;
using System.Net.Sockets;
namespace LiteNetLib.Utils
{
internal sealed class NtpRequest
{
private const int ResendTimer = 1000;
private const int KillTimer = 10000;
public const int DefaultPort = 123;
private readonly IPEndPoint _ntpEndPoint;
private int _resendTime = ResendTimer;
private int _killTime = 0;
public NtpRequest(IPEndPoint endPoint)
{
_ntpEndPoint = endPoint;
}
public bool NeedToKill => _killTime >= KillTimer;
public bool Send(Socket socket, int time)
{
_resendTime += time;
_killTime += time;
if (_resendTime < ResendTimer)
{
return false;
}
var packet = new NtpPacket();
try
{
int sendCount = socket.SendTo(packet.Bytes, 0, packet.Bytes.Length, SocketFlags.None, _ntpEndPoint);
return sendCount == packet.Bytes.Length;
}
catch
{
return false;
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2feec8d850902ea4daaad8d9b1dec189
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: