You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
CrowdControl/Assets/Plugins/ConsolePro/Remote/LiteNetLib/NetSerializer.cs

710 lines
30 KiB
C#

1 month ago
#if DEBUG && !UNITY_WP_8_1 && !UNITY_WSA
using System;
using System.Reflection;
using System.Collections.Generic;
#if WINRT || NETCORE
using System.Linq;
#endif
namespace FlyingWormConsole3.LiteNetLib.Utils
{
public interface INetSerializable
{
void Serialize(NetDataWriter writer);
void Desereialize(NetDataReader reader);
}
public abstract class NetSerializerHasher
{
public abstract ulong GetHash(string type);
public abstract void WriteHash(ulong hash, NetDataWriter writer);
public abstract ulong ReadHash(NetDataReader reader);
}
public sealed class FNVHasher : NetSerializerHasher
{
private readonly Dictionary<string, ulong> _hashCache = new Dictionary<string, ulong>();
private readonly char[] _hashBuffer = new char[1024];
public override ulong GetHash(string type)
{
ulong hash;
if (_hashCache.TryGetValue(type, out hash))
{
return hash;
}
hash = 14695981039346656037UL; //offset
int len = type.Length;
type.CopyTo(0, _hashBuffer, 0, len);
for (var i = 0; i < len; i++)
{
hash = hash ^ _hashBuffer[i];
hash *= 1099511628211UL; //prime
}
_hashCache.Add(type, hash);
return hash;
}
public override ulong ReadHash(NetDataReader reader)
{
return reader.GetULong();
}
public override void WriteHash(ulong hash, NetDataWriter writer)
{
writer.Put(hash);
}
}
public sealed class NetSerializer
{
private sealed class CustomType
{
public readonly CustomTypeWrite WriteDelegate;
public readonly CustomTypeRead ReadDelegate;
public CustomType(CustomTypeWrite writeDelegate, CustomTypeRead readDelegate)
{
WriteDelegate = writeDelegate;
ReadDelegate = readDelegate;
}
}
private delegate void CustomTypeWrite(NetDataWriter writer, object customObj);
private delegate object CustomTypeRead(NetDataReader reader);
private sealed class StructInfo
{
public readonly Action<NetDataWriter>[] WriteDelegate;
public readonly Action<NetDataReader>[] ReadDelegate;
public readonly Type[] FieldTypes;
public object Reference;
public Func<object> CreatorFunc;
public Action<object, object> OnReceive;
public readonly ulong Hash;
public readonly int MembersCount;
public StructInfo(ulong hash, int membersCount)
{
Hash = hash;
MembersCount = membersCount;
WriteDelegate = new Action<NetDataWriter>[membersCount];
ReadDelegate = new Action<NetDataReader>[membersCount];
FieldTypes = new Type[membersCount];
}
public void Write(NetDataWriter writer, object obj)
{
Reference = obj;
for (int i = 0; i < MembersCount; i++)
{
WriteDelegate[i](writer);
}
}
public void Read(NetDataReader reader)
{
for (int i = 0; i < MembersCount; i++)
{
ReadDelegate[i](reader);
}
}
}
private readonly Dictionary<ulong, StructInfo> _cache;
private readonly Dictionary<Type, CustomType> _registeredCustomTypes;
private static readonly HashSet<Type> BasicTypes = new HashSet<Type>
{
typeof(int),
typeof(uint),
typeof(byte),
typeof(sbyte),
typeof(short),
typeof(ushort),
typeof(long),
typeof(ulong),
typeof(string),
typeof(float),
typeof(double),
typeof(bool)
};
private readonly NetDataWriter _writer;
private readonly NetSerializerHasher _hasher;
private const int MaxStringLenght = 1024;
public NetSerializer() : this(new FNVHasher())
{
}
public NetSerializer(NetSerializerHasher hasher)
{
_hasher = hasher;
_cache = new Dictionary<ulong, StructInfo>();
_registeredCustomTypes = new Dictionary<Type, CustomType>();
_writer = new NetDataWriter();
}
private bool RegisterCustomTypeInternal<T>(Func<T> constructor) where T : INetSerializable
{
var t = typeof(T);
if (_registeredCustomTypes.ContainsKey(t))
{
return false;
}
var rwDelegates = new CustomType(
(writer, obj) =>
{
((T)obj).Serialize(writer);
},
reader =>
{
var instance = constructor();
instance.Desereialize(reader);
return instance;
});
_registeredCustomTypes.Add(t, rwDelegates);
return true;
}
/// <summary>
/// Register custom property type
/// </summary>
/// <typeparam name="T">INetSerializable structure</typeparam>
/// <returns>True - if register successful, false - if type already registered</returns>
public bool RegisterCustomType<T>() where T : struct, INetSerializable
{
return RegisterCustomTypeInternal(() => new T());
}
/// <summary>
/// Register custom property type
/// </summary>
/// <typeparam name="T">INetSerializable class</typeparam>
/// <returns>True - if register successful, false - if type already registered</returns>
public bool RegisterCustomType<T>(Func<T> constructor) where T : class, INetSerializable
{
return RegisterCustomTypeInternal(constructor);
}
/// <summary>
/// Register custom property type
/// </summary>
/// <param name="writeDelegate"></param>
/// <param name="readDelegate"></param>
/// <returns>True - if register successful, false - if type already registered</returns>
public bool RegisterCustomType<T>(Action<NetDataWriter, T> writeDelegate, Func<NetDataReader, T> readDelegate)
{
var t = typeof(T);
if (BasicTypes.Contains(t) || _registeredCustomTypes.ContainsKey(t))
{
return false;
}
var rwDelegates = new CustomType(
(writer, obj) => writeDelegate(writer, (T)obj),
reader => readDelegate(reader));
_registeredCustomTypes.Add(t, rwDelegates);
return true;
}
private static Delegate CreateDelegate(Type type, MethodInfo info)
{
#if WINRT || NETCORE
return info.CreateDelegate(type);
#else
return Delegate.CreateDelegate(type, info);
#endif
}
private static Func<TClass, TProperty> ExtractGetDelegate<TClass, TProperty>(MethodInfo info)
{
return (Func<TClass, TProperty>)CreateDelegate(typeof(Func<TClass, TProperty>), info);
}
private static Action<TClass, TProperty> ExtractSetDelegate<TClass, TProperty>(MethodInfo info)
{
return (Action<TClass, TProperty>)CreateDelegate(typeof(Action<TClass, TProperty>), info);
}
private StructInfo RegisterInternal<T>() where T : class
{
Type t = typeof(T);
ulong nameHash = _hasher.GetHash(t.Name);
StructInfo info;
if (_cache.TryGetValue(nameHash, out info))
{
return info;
}
#if WINRT || NETCORE
var props = t.GetRuntimeProperties().ToArray();
int propsCount = props.Count();
#else
var props = t.GetProperties(
BindingFlags.Instance |
BindingFlags.Public |
BindingFlags.GetProperty |
BindingFlags.SetProperty);
int propsCount = props.Length;
#endif
if (props == null || propsCount < 0)
{
throw new ArgumentException("Type does not contain acceptable fields");
}
info = new StructInfo(nameHash, propsCount);
for (int i = 0; i < props.Length; i++)
{
var property = props[i];
var propertyType = property.PropertyType;
//Set field type
info.FieldTypes[i] = propertyType.IsArray ? propertyType.GetElementType() : propertyType;
#if WINRT || NETCORE
bool isEnum = propertyType.GetTypeInfo().IsEnum;
var getMethod = property.GetMethod;
var setMethod = property.SetMethod;
#else
bool isEnum = propertyType.IsEnum;
var getMethod = property.GetGetMethod();
var setMethod = property.GetSetMethod();
#endif
if (isEnum)
{
var underlyingType = Enum.GetUnderlyingType(propertyType);
if (underlyingType == typeof(byte))
{
info.ReadDelegate[i] = reader =>
{
property.SetValue(info.Reference, Enum.ToObject(propertyType, reader.GetByte()), null);
};
info.WriteDelegate[i] = writer =>
{
writer.Put((byte)property.GetValue(info.Reference, null));
};
}
else if (underlyingType == typeof(int))
{
info.ReadDelegate[i] = reader =>
{
property.SetValue(info.Reference, Enum.ToObject(propertyType, reader.GetInt()), null);
};
info.WriteDelegate[i] = writer =>
{
writer.Put((int)property.GetValue(info.Reference, null));
};
}
else
{
throw new Exception("Not supported enum underlying type: " + underlyingType.Name);
}
}
else if (propertyType == typeof(string))
{
var setDelegate = ExtractSetDelegate<T, string>(setMethod);
var getDelegate = ExtractGetDelegate<T, string>(getMethod);
info.ReadDelegate[i] = reader => setDelegate((T)info.Reference, reader.GetString(MaxStringLenght));
info.WriteDelegate[i] = writer => writer.Put(getDelegate((T)info.Reference), MaxStringLenght);
}
else if (propertyType == typeof(bool))
{
var setDelegate = ExtractSetDelegate<T, bool>(setMethod);
var getDelegate = ExtractGetDelegate<T, bool>(getMethod);
info.ReadDelegate[i] = reader => setDelegate((T)info.Reference, reader.GetBool());
info.WriteDelegate[i] = writer => writer.Put(getDelegate((T)info.Reference));
}
else if (propertyType == typeof(byte))
{
var setDelegate = ExtractSetDelegate<T, byte>(setMethod);
var getDelegate = ExtractGetDelegate<T, byte>(getMethod);
info.ReadDelegate[i] = reader => setDelegate((T)info.Reference, reader.GetByte());
info.WriteDelegate[i] = writer => writer.Put(getDelegate((T)info.Reference));
}
else if (propertyType == typeof(sbyte))
{
var setDelegate = ExtractSetDelegate<T, sbyte>(setMethod);
var getDelegate = ExtractGetDelegate<T, sbyte>(getMethod);
info.ReadDelegate[i] = reader => setDelegate((T)info.Reference, reader.GetSByte());
info.WriteDelegate[i] = writer => writer.Put(getDelegate((T)info.Reference));
}
else if (propertyType == typeof(short))
{
var setDelegate = ExtractSetDelegate<T, short>(setMethod);
var getDelegate = ExtractGetDelegate<T, short>(getMethod);
info.ReadDelegate[i] = reader => setDelegate((T)info.Reference, reader.GetShort());
info.WriteDelegate[i] = writer => writer.Put(getDelegate((T)info.Reference));
}
else if (propertyType == typeof(ushort))
{
var setDelegate = ExtractSetDelegate<T, ushort>(setMethod);
var getDelegate = ExtractGetDelegate<T, ushort>(getMethod);
info.ReadDelegate[i] = reader => setDelegate((T)info.Reference, reader.GetUShort());
info.WriteDelegate[i] = writer => writer.Put(getDelegate((T)info.Reference));
}
else if (propertyType == typeof(int))
{
var setDelegate = ExtractSetDelegate<T, int>(setMethod);
var getDelegate = ExtractGetDelegate<T, int>(getMethod);
info.ReadDelegate[i] = reader => setDelegate((T)info.Reference, reader.GetInt());
info.WriteDelegate[i] = writer => writer.Put(getDelegate((T)info.Reference));
}
else if (propertyType == typeof(uint))
{
var setDelegate = ExtractSetDelegate<T, uint>(setMethod);
var getDelegate = ExtractGetDelegate<T, uint>(getMethod);
info.ReadDelegate[i] = reader => setDelegate((T)info.Reference, reader.GetUInt());
info.WriteDelegate[i] = writer => writer.Put(getDelegate((T)info.Reference));
}
else if (propertyType == typeof(long))
{
var setDelegate = ExtractSetDelegate<T, long>(setMethod);
var getDelegate = ExtractGetDelegate<T, long>(getMethod);
info.ReadDelegate[i] = reader => setDelegate((T)info.Reference, reader.GetLong());
info.WriteDelegate[i] = writer => writer.Put(getDelegate((T)info.Reference));
}
else if (propertyType == typeof(ulong))
{
var setDelegate = ExtractSetDelegate<T, ulong>(setMethod);
var getDelegate = ExtractGetDelegate<T, ulong>(getMethod);
info.ReadDelegate[i] = reader => setDelegate((T)info.Reference, reader.GetULong());
info.WriteDelegate[i] = writer => writer.Put(getDelegate((T)info.Reference));
}
else if (propertyType == typeof(float))
{
var setDelegate = ExtractSetDelegate<T, float>(setMethod);
var getDelegate = ExtractGetDelegate<T, float>(getMethod);
info.ReadDelegate[i] = reader => setDelegate((T)info.Reference, reader.GetFloat());
info.WriteDelegate[i] = writer => writer.Put(getDelegate((T)info.Reference));
}
else if (propertyType == typeof(double))
{
var setDelegate = ExtractSetDelegate<T, double>(setMethod);
var getDelegate = ExtractGetDelegate<T, double>(getMethod);
info.ReadDelegate[i] = reader => setDelegate((T)info.Reference, reader.GetDouble());
info.WriteDelegate[i] = writer => writer.Put(getDelegate((T)info.Reference));
}
// Array types
else if (propertyType == typeof(string[]))
{
var setDelegate = ExtractSetDelegate<T, string[]>(setMethod);
var getDelegate = ExtractGetDelegate<T, string[]>(getMethod);
info.ReadDelegate[i] = reader => setDelegate((T)info.Reference, reader.GetStringArray(MaxStringLenght));
info.WriteDelegate[i] = writer => writer.PutArray(getDelegate((T)info.Reference), MaxStringLenght);
}
else if (propertyType == typeof(byte[]))
{
var setDelegate = ExtractSetDelegate<T, byte[]>(setMethod);
var getDelegate = ExtractGetDelegate<T, byte[]>(getMethod);
info.ReadDelegate[i] = reader => setDelegate((T)info.Reference, reader.GetBytesWithLength());
info.WriteDelegate[i] = writer => writer.PutBytesWithLength(getDelegate((T)info.Reference));
}
else if (propertyType == typeof(short[]))
{
var setDelegate = ExtractSetDelegate<T, short[]>(setMethod);
var getDelegate = ExtractGetDelegate<T, short[]>(getMethod);
info.ReadDelegate[i] = reader => setDelegate((T)info.Reference, reader.GetShortArray());
info.WriteDelegate[i] = writer => writer.PutArray(getDelegate((T)info.Reference));
}
else if (propertyType == typeof(ushort[]))
{
var setDelegate = ExtractSetDelegate<T, ushort[]>(setMethod);
var getDelegate = ExtractGetDelegate<T, ushort[]>(getMethod);
info.ReadDelegate[i] = reader => setDelegate((T)info.Reference, reader.GetUShortArray());
info.WriteDelegate[i] = writer => writer.PutArray(getDelegate((T)info.Reference));
}
else if (propertyType == typeof(int[]))
{
var setDelegate = ExtractSetDelegate<T, int[]>(setMethod);
var getDelegate = ExtractGetDelegate<T, int[]>(getMethod);
info.ReadDelegate[i] = reader => setDelegate((T)info.Reference, reader.GetIntArray());
info.WriteDelegate[i] = writer => writer.PutArray(getDelegate((T)info.Reference));
}
else if (propertyType == typeof(uint[]))
{
var setDelegate = ExtractSetDelegate<T, uint[]>(setMethod);
var getDelegate = ExtractGetDelegate<T, uint[]>(getMethod);
info.ReadDelegate[i] = reader => setDelegate((T)info.Reference, reader.GetUIntArray());
info.WriteDelegate[i] = writer => writer.PutArray(getDelegate((T)info.Reference));
}
else if (propertyType == typeof(long[]))
{
var setDelegate = ExtractSetDelegate<T, long[]>(setMethod);
var getDelegate = ExtractGetDelegate<T, long[]>(getMethod);
info.ReadDelegate[i] = reader => setDelegate((T)info.Reference, reader.GetLongArray());
info.WriteDelegate[i] = writer => writer.PutArray(getDelegate((T)info.Reference));
}
else if (propertyType == typeof(ulong[]))
{
var setDelegate = ExtractSetDelegate<T, ulong[]>(setMethod);
var getDelegate = ExtractGetDelegate<T, ulong[]>(getMethod);
info.ReadDelegate[i] = reader => setDelegate((T)info.Reference, reader.GetULongArray());
info.WriteDelegate[i] = writer => writer.PutArray(getDelegate((T)info.Reference));
}
else if (propertyType == typeof(float[]))
{
var setDelegate = ExtractSetDelegate<T, float[]>(setMethod);
var getDelegate = ExtractGetDelegate<T, float[]>(getMethod);
info.ReadDelegate[i] = reader => setDelegate((T)info.Reference, reader.GetFloatArray());
info.WriteDelegate[i] = writer => writer.PutArray(getDelegate((T)info.Reference));
}
else if (propertyType == typeof(double[]))
{
var setDelegate = ExtractSetDelegate<T, double[]>(setMethod);
var getDelegate = ExtractGetDelegate<T, double[]>(getMethod);
info.ReadDelegate[i] = reader => setDelegate((T)info.Reference, reader.GetDoubleArray());
info.WriteDelegate[i] = writer => writer.PutArray(getDelegate((T)info.Reference));
}
else
{
CustomType registeredCustomType;
bool array = false;
if (propertyType.IsArray)
{
array = true;
propertyType = propertyType.GetElementType();
}
if (_registeredCustomTypes.TryGetValue(propertyType, out registeredCustomType))
{
if (array) //Array type serialize/deserialize
{
info.ReadDelegate[i] = reader =>
{
ushort arrLength = reader.GetUShort();
Array arr = Array.CreateInstance(propertyType, arrLength);
for (int k = 0; k < arrLength; k++)
{
arr.SetValue(registeredCustomType.ReadDelegate(reader), k);
}
property.SetValue(info.Reference, arr, null);
};
info.WriteDelegate[i] = writer =>
{
Array arr = (Array)property.GetValue(info.Reference, null);
writer.Put((ushort)arr.Length);
for (int k = 0; k < arr.Length; k++)
{
registeredCustomType.WriteDelegate(writer, arr.GetValue(k));
}
};
}
else //Simple
{
info.ReadDelegate[i] = reader =>
{
property.SetValue(info.Reference, registeredCustomType.ReadDelegate(reader), null);
};
info.WriteDelegate[i] = writer =>
{
registeredCustomType.WriteDelegate(writer, property.GetValue(info.Reference, null));
};
}
}
else
{
throw new Exception("Unknown property type: " + propertyType.Name);
}
}
}
_cache.Add(nameHash, info);
return info;
}
/// <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>
public void ReadAllPackets<T>(NetDataReader reader, T 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>
public void ReadPacket(NetDataReader reader)
{
ReadPacket(reader, null);
}
private StructInfo ReadInfo(NetDataReader reader)
{
ulong hash = _hasher.ReadHash(reader);
StructInfo info;
if (!_cache.TryGetValue(hash, out info))
{
throw new Exception("Undefined packet received");
}
return info;
}
/// <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>
public T ReadKnownPacket<T>(NetDataReader reader) where T : class, new()
{
var info = ReadInfo(reader);
ulong typeHash = _hasher.GetHash(typeof(T).Name);
if (typeHash != info.Hash)
{
return null;
}
info.Reference = info.CreatorFunc != null ? info.CreatorFunc() : Activator.CreateInstance<T>();
info.Read(reader);
return (T)info.Reference;
}
/// <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>
public bool ReadKnownPacket<T>(NetDataReader reader, T target) where T : class, new()
{
var info = ReadInfo(reader);
ulong typeHash = _hasher.GetHash(typeof(T).Name);
if (typeHash != info.Hash)
{
return false;
}
info.Reference = target;
info.Read(reader);
return true;
}
/// <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>
public void ReadPacket(NetDataReader reader, object userData)
{
var info = ReadInfo(reader);
if (info.CreatorFunc != null)
{
info.Reference = info.CreatorFunc();
}
info.Read(reader);
info.OnReceive(info.Reference, 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 intead of slow Activator.CreateInstance</param>
public void Subscribe<T>(Action<T> onReceive, Func<T> packetConstructor) where T : class, new()
{
var info = RegisterInternal<T>();
info.CreatorFunc = () => packetConstructor();
info.OnReceive = (o, userData) => { onReceive((T)o); };
}
/// <summary>
/// Register packet type for direct reading (ReadKnownPacket)
/// </summary>
/// <param name="packetConstructor">Method that constructs packet intead of slow Activator.CreateInstance</param>
public void Register<T>(Func<T> packetConstructor = null) where T : class, new()
{
var info = RegisterInternal<T>();
if (packetConstructor != null)
{
info.CreatorFunc = () => packetConstructor();
}
info.OnReceive = (o, userData) => { };
}
/// <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 intead of slow Activator.CreateInstance</param>
public void Subscribe<T, TUserData>(Action<T, TUserData> onReceive, Func<T> packetConstructor) where T : class, new()
{
var info = RegisterInternal<T>();
info.CreatorFunc = () => packetConstructor();
info.OnReceive = (o, userData) => { onReceive((T)o, (TUserData)userData); };
}
/// <summary>
/// Register and subscribe to packet receive event
/// This metod 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>
public void SubscribeReusable<T>(Action<T> onReceive) where T : class, new()
{
var info = RegisterInternal<T>();
info.Reference = new T();
info.OnReceive = (o, userData) => { onReceive((T)o); };
}
/// <summary>
/// Register and subscribe to packet receive event
/// This metod 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>
public void SubscribeReusable<T, TUserData>(Action<T, TUserData> onReceive) where T : class, new()
{
var info = RegisterInternal<T>();
info.Reference = new T();
info.OnReceive = (o, userData) => { onReceive((T)o, (TUserData)userData); };
}
/// <summary>
/// Serialize struct to NetDataWriter (fast)
/// </summary>
/// <param name="writer">Serialization target NetDataWriter</param>
/// <param name="obj">Struct to serialize</param>
public void Serialize<T>(NetDataWriter writer, T obj) where T : class, new()
{
var info = RegisterInternal<T>();
_hasher.WriteHash(info.Hash, writer);
info.Write(writer, obj);
}
/// <summary>
/// Serialize struct to byte array
/// </summary>
/// <param name="obj">Struct to serialize</param>
/// <returns>byte array with serialized data</returns>
public byte[] Serialize<T>(T obj) where T : class, new()
{
_writer.Reset();
Serialize(_writer, obj);
return _writer.CopyData();
}
}
}
#endif