using System; using System.Reflection; using System.Collections.Generic; using System.Net; 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 { 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 : FastCall { protected Func Getter; protected Action Setter; protected Func GetterArr; protected Action SetterArr; protected Func> GetterList; protected Action> 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 ReadListHelper(TClass inf, NetDataReader r, out int len) { len = r.GetUShort(); var list = GetterList(inf); if (list == null) { list = new List(len); SetterList(inf, list); } return list; } protected List 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)Delegate.CreateDelegate(typeof(Func), getMethod); SetterArr = (Action)Delegate.CreateDelegate(typeof(Action), setMethod); break; case CallType.List: GetterList = (Func>)Delegate.CreateDelegate(typeof(Func>), getMethod); SetterList = (Action>)Delegate.CreateDelegate(typeof(Action>), setMethod); break; default: Getter = (Func)Delegate.CreateDelegate(typeof(Func), getMethod); Setter = (Action)Delegate.CreateDelegate(typeof(Action), setMethod); break; } } } private abstract class FastCallSpecificAuto : FastCallSpecific { 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) { TProperty elem; ElementRead(r, out 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 : FastCallSpecific { private readonly Action _writer; private readonly Func _reader; public FastCallStatic(Action write, Func 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) { int len; var list = ReadListHelper(inf, r, out len); int listCount = list.Count; if (len > listCount) { for (int i = 0; i < listCount; i++) list[i] = _reader(r); for (int i = listCount; i < len; i++) list.Add(_reader(r)); return; } if (len < listCount) list.RemoveRange(len, listCount - len); for (int i = 0; i < len; i++) list[i] = _reader(r); } public override void WriteList(TClass inf, NetDataWriter w) { int len; var list = WriteListHelper(inf, w, out 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 : FastCallSpecific 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) { int len; var list = ReadListHelper(inf, r, out len); int listCount = list.Count; if (len > listCount) { for (int i = 0; i < listCount; i++) list[i].Deserialize(r); for (int i = listCount; i < len; i++) { var itm = default(TProperty); itm.Deserialize(r); list.Add(itm); } return; } if(len < listCount) list.RemoveRange(len, listCount - len); for (int i = 0; i < len; i++) list[i].Deserialize(r); } public override void WriteList(TClass inf, NetDataWriter w) { int len; var list = WriteListHelper(inf, w, out 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 : FastCallSpecific where TProperty : class, INetSerializable { private readonly Func _constructor; public FastCallClass(Func 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); if(p != null) p.Serialize(w); } public override void ReadList(TClass inf, NetDataReader r) { int len; var list = ReadListHelper(inf, r, out len); int listCount = list.Count; if (len > listCount) { for (int i = 0; i < listCount; i++) list[i].Deserialize(r); for (int i = listCount; i < len; i++) { var itm = _constructor(); itm.Deserialize(r); list.Add(itm); } return; } if (len < listCount) list.RemoveRange(len, listCount - len); for (int i = 0; i < len; i++) list[i].Deserialize(r); } public override void WriteList(TClass inf, NetDataWriter w) { int len; var list = WriteListHelper(inf, w, out 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 : FastCallSpecific { 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 : FastCallSpecific { 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 : FastCallSpecific { 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 : FastCallSpecific { 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 : FastCallSpecific { 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 : FastCallSpecific { 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 : FastCallSpecific { 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 : FastCallSpecific { 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 : FastCallSpecific { 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 : FastCallSpecific { 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 : FastCallSpecific { 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 : FastCallSpecificAuto { 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 : FastCallSpecificAuto { 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 : FastCallSpecific { 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 : FastCall { 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"); } public override void WriteList(T inf, NetDataWriter w) { throw new InvalidTypeException("Unsupported type: List"); } } private class EnumIntSerializer : EnumByteSerializer { 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 { public static ClassInfo Instance; private readonly FastCall[] _serializers; private readonly int _membersCount; public ClassInfo(List> 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 Get(); } private sealed class CustomTypeStruct : CustomType where TProperty : struct, INetSerializable { public override FastCall Get() { return new FastCallStruct(); } } private sealed class CustomTypeClass : CustomType where TProperty : class, INetSerializable { private readonly Func _constructor; public CustomTypeClass(Func constructor) { _constructor = constructor; } public override FastCall Get() { return new FastCallClass(_constructor); } } private sealed class CustomTypeStatic : CustomType { private readonly Action _writer; private readonly Func _reader; public CustomTypeStatic(Action writer, Func reader) { _writer = writer; _reader = reader; } public override FastCall Get() { return new FastCallStatic(_writer, _reader); } } /// /// Register custom property type /// /// INetSerializable structure public void RegisterNestedType() where T : struct, INetSerializable { _registeredTypes.Add(typeof(T), new CustomTypeStruct()); } /// /// Register custom property type /// /// INetSerializable class public void RegisterNestedType(Func constructor) where T : class, INetSerializable { _registeredTypes.Add(typeof(T), new CustomTypeClass(constructor)); } /// /// Register custom property type /// /// Any packet /// custom type writer /// custom type reader public void RegisterNestedType(Action writer, Func reader) { _registeredTypes.Add(typeof(T), new CustomTypeStatic(writer, reader)); } private NetDataWriter _writer; private readonly int _maxStringLength; private readonly Dictionary _registeredTypes = new Dictionary(); public NetSerializer() : this(0) { } public NetSerializer(int maxStringLength) { _maxStringLength = maxStringLength; } private ClassInfo RegisterInternal() { if (ClassInfo.Instance != null) return ClassInfo.Instance; Type t = typeof(T); var props = t.GetProperties( BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetProperty | BindingFlags.SetProperty); var serializers = new List>(); 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; } var getMethod = property.GetGetMethod(); var setMethod = property.GetSetMethod(); if (getMethod == null || setMethod == null) continue; FastCall serialzer = null; if (propertyType.IsEnum) { var underlyingType = Enum.GetUnderlyingType(propertyType); if (underlyingType == typeof(byte)) serialzer = new EnumByteSerializer(property, propertyType); else if (underlyingType == typeof(int)) serialzer = new EnumIntSerializer(property, propertyType); else throw new InvalidTypeException("Not supported enum underlying type: " + underlyingType.Name); } else if (elementType == typeof(string)) serialzer = new StringSerializer(_maxStringLength); else if (elementType == typeof(bool)) serialzer = new BoolSerializer(); else if (elementType == typeof(byte)) serialzer = new ByteSerializer(); else if (elementType == typeof(sbyte)) serialzer = new SByteSerializer(); else if (elementType == typeof(short)) serialzer = new ShortSerializer(); else if (elementType == typeof(ushort)) serialzer = new UShortSerializer(); else if (elementType == typeof(int)) serialzer = new IntSerializer(); else if (elementType == typeof(uint)) serialzer = new UIntSerializer(); else if (elementType == typeof(long)) serialzer = new LongSerializer(); else if (elementType == typeof(ulong)) serialzer = new ULongSerializer(); else if (elementType == typeof(float)) serialzer = new FloatSerializer(); else if (elementType == typeof(double)) serialzer = new DoubleSerializer(); else if (elementType == typeof(char)) serialzer = new CharSerializer(); else if (elementType == typeof(IPEndPoint)) serialzer = new IPEndPointSerializer(); else { CustomType customType; _registeredTypes.TryGetValue(elementType, out customType); if (customType != null) serialzer = customType.Get(); } if (serialzer != null) { serialzer.Init(getMethod, setMethod, callType); serializers.Add(serialzer); } else { throw new InvalidTypeException("Unknown property type: " + propertyType.FullName); } } ClassInfo.Instance = new ClassInfo(serializers); return ClassInfo.Instance; } /// 's fields are not supported, or it has no fields public void Register() { RegisterInternal(); } /// /// Reads packet with known type /// /// NetDataReader with packet /// Returns packet if packet in reader is matched type /// 's fields are not supported, or it has no fields public T Deserialize(NetDataReader reader) where T : class, new() { var info = RegisterInternal(); var result = new T(); try { info.Read(result, reader); } catch { return null; } return result; } /// /// Reads packet with known type (non alloc variant) /// /// NetDataReader with packet /// Deserialization target /// Returns true if packet in reader is matched type /// 's fields are not supported, or it has no fields public bool Deserialize(NetDataReader reader, T target) where T : class, new() { var info = RegisterInternal(); try { info.Read(target, reader); } catch { return false; } return true; } /// /// Serialize object to NetDataWriter (fast) /// /// Serialization target NetDataWriter /// Object to serialize /// 's fields are not supported, or it has no fields public void Serialize(NetDataWriter writer, T obj) where T : class, new() { RegisterInternal().Write(obj, writer); } /// /// Serialize object to byte array /// /// Object to serialize /// byte array with serialized data public byte[] Serialize(T obj) where T : class, new() { if (_writer == null) _writer = new NetDataWriter(); _writer.Reset(); Serialize(_writer, obj); return _writer.CopyData(); } } }