#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 _hashCache = new Dictionary(); 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[] WriteDelegate; public readonly Action[] ReadDelegate; public readonly Type[] FieldTypes; public object Reference; public Func CreatorFunc; public Action OnReceive; public readonly ulong Hash; public readonly int MembersCount; public StructInfo(ulong hash, int membersCount) { Hash = hash; MembersCount = membersCount; WriteDelegate = new Action[membersCount]; ReadDelegate = new Action[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 _cache; private readonly Dictionary _registeredCustomTypes; private static readonly HashSet BasicTypes = new HashSet { 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(); _registeredCustomTypes = new Dictionary(); _writer = new NetDataWriter(); } private bool RegisterCustomTypeInternal(Func 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; } /// /// Register custom property type /// /// INetSerializable structure /// True - if register successful, false - if type already registered public bool RegisterCustomType() where T : struct, INetSerializable { return RegisterCustomTypeInternal(() => new T()); } /// /// Register custom property type /// /// INetSerializable class /// True - if register successful, false - if type already registered public bool RegisterCustomType(Func constructor) where T : class, INetSerializable { return RegisterCustomTypeInternal(constructor); } /// /// Register custom property type /// /// /// /// True - if register successful, false - if type already registered public bool RegisterCustomType(Action writeDelegate, Func 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 ExtractGetDelegate(MethodInfo info) { return (Func)CreateDelegate(typeof(Func), info); } private static Action ExtractSetDelegate(MethodInfo info) { return (Action)CreateDelegate(typeof(Action), info); } private StructInfo RegisterInternal() 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(setMethod); var getDelegate = ExtractGetDelegate(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(setMethod); var getDelegate = ExtractGetDelegate(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(setMethod); var getDelegate = ExtractGetDelegate(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(setMethod); var getDelegate = ExtractGetDelegate(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(setMethod); var getDelegate = ExtractGetDelegate(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(setMethod); var getDelegate = ExtractGetDelegate(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(setMethod); var getDelegate = ExtractGetDelegate(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(setMethod); var getDelegate = ExtractGetDelegate(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(setMethod); var getDelegate = ExtractGetDelegate(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(setMethod); var getDelegate = ExtractGetDelegate(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(setMethod); var getDelegate = ExtractGetDelegate(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(setMethod); var getDelegate = ExtractGetDelegate(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(setMethod); var getDelegate = ExtractGetDelegate(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(setMethod); var getDelegate = ExtractGetDelegate(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(setMethod); var getDelegate = ExtractGetDelegate(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(setMethod); var getDelegate = ExtractGetDelegate(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(setMethod); var getDelegate = ExtractGetDelegate(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(setMethod); var getDelegate = ExtractGetDelegate(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(setMethod); var getDelegate = ExtractGetDelegate(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(setMethod); var getDelegate = ExtractGetDelegate(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(setMethod); var getDelegate = ExtractGetDelegate(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(setMethod); var getDelegate = ExtractGetDelegate(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; } /// /// Reads all available data from NetDataReader and calls OnReceive delegates /// /// NetDataReader with packets data public void ReadAllPackets(NetDataReader reader) { while (reader.AvailableBytes > 0) { ReadPacket(reader); } } /// /// Reads all available data from NetDataReader and calls OnReceive delegates /// /// NetDataReader with packets data /// Argument that passed to OnReceivedEvent public void ReadAllPackets(NetDataReader reader, T userData) { while (reader.AvailableBytes > 0) { ReadPacket(reader, userData); } } /// /// Reads one packet from NetDataReader and calls OnReceive delegate /// /// NetDataReader with packet 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; } /// /// Reads packet with known type /// /// NetDataReader with packet /// Returns packet if packet in reader is matched type public T ReadKnownPacket(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(); info.Read(reader); return (T)info.Reference; } /// /// Reads packet with known type (non alloc variant) /// /// NetDataReader with packet /// Deserialization target /// Returns true if packet in reader is matched type public bool ReadKnownPacket(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; } /// /// Reads one packet from NetDataReader and calls OnReceive delegate /// /// NetDataReader with packet /// Argument that passed to OnReceivedEvent 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); } /// /// Register and subscribe to packet receive event /// /// event that will be called when packet deserialized with ReadPacket method /// Method that constructs packet intead of slow Activator.CreateInstance public void Subscribe(Action onReceive, Func packetConstructor) where T : class, new() { var info = RegisterInternal(); info.CreatorFunc = () => packetConstructor(); info.OnReceive = (o, userData) => { onReceive((T)o); }; } /// /// Register packet type for direct reading (ReadKnownPacket) /// /// Method that constructs packet intead of slow Activator.CreateInstance public void Register(Func packetConstructor = null) where T : class, new() { var info = RegisterInternal(); if (packetConstructor != null) { info.CreatorFunc = () => packetConstructor(); } info.OnReceive = (o, userData) => { }; } /// /// Register and subscribe to packet receive event (with userData) /// /// event that will be called when packet deserialized with ReadPacket method /// Method that constructs packet intead of slow Activator.CreateInstance public void Subscribe(Action onReceive, Func packetConstructor) where T : class, new() { var info = RegisterInternal(); info.CreatorFunc = () => packetConstructor(); info.OnReceive = (o, userData) => { onReceive((T)o, (TUserData)userData); }; } /// /// Register and subscribe to packet receive event /// This metod will overwrite last received packet class on receive (less garbage) /// /// event that will be called when packet deserialized with ReadPacket method public void SubscribeReusable(Action onReceive) where T : class, new() { var info = RegisterInternal(); info.Reference = new T(); info.OnReceive = (o, userData) => { onReceive((T)o); }; } /// /// Register and subscribe to packet receive event /// This metod will overwrite last received packet class on receive (less garbage) /// /// event that will be called when packet deserialized with ReadPacket method public void SubscribeReusable(Action onReceive) where T : class, new() { var info = RegisterInternal(); info.Reference = new T(); info.OnReceive = (o, userData) => { onReceive((T)o, (TUserData)userData); }; } /// /// Serialize struct to NetDataWriter (fast) /// /// Serialization target NetDataWriter /// Struct to serialize public void Serialize(NetDataWriter writer, T obj) where T : class, new() { var info = RegisterInternal(); _hasher.WriteHash(info.Hash, writer); info.Write(writer, obj); } /// /// Serialize struct to byte array /// /// Struct to serialize /// byte array with serialized data public byte[] Serialize(T obj) where T : class, new() { _writer.Reset(); Serialize(_writer, obj); return _writer.CopyData(); } } } #endif