#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