using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.Serialization; using Godot; /// /// Implements a simple de/serializer based on a serialize and deserialize /// function, typically specified using short lambda expressions. /// A shortcut method for creating an instance of this class can be found at: /// . /// public class SimpleDeSerializer : DeSerializer { private readonly Action _serialize; private readonly Func _deserialize; public SimpleDeSerializer(Action serialize, Func deserialize) { _serialize = serialize; _deserialize = deserialize; } public override void Serialize(Game game, BinaryWriter writer, T value) => _serialize(writer, value); public override T Deserialize(Game game, BinaryReader reader) => _deserialize(reader); } public class EnumDeSerializerGenerator : IDeSerializerGenerator { public IDeSerializer GenerateFor(Type type) { // TODO: Flagged enums are not supported at this time. if (!type.IsEnum || (type.GetCustomAttribute() != null)) return null; var deSerializerType = typeof(EnumDeSerializer<,>).MakeGenericType(type, type.GetEnumUnderlyingType()); return (IDeSerializer)Activator.CreateInstance(deSerializerType); } private class EnumDeSerializer : DeSerializer where TEnum : Enum { private readonly IDeSerializer _underlyingDeSerializer = DeSerializerRegistry.Get(true); public override void Serialize(Game game, BinaryWriter writer, TEnum value) { if (!Enum.IsDefined(typeof(TEnum), value)) throw new ArgumentException( $"Invalid enum value {value} for type {typeof(TEnum)}", nameof(value)); _underlyingDeSerializer.Serialize(game, writer, (TUnderlying)(object)value); } public override TEnum Deserialize(Game game, BinaryReader reader) { var value = (TEnum)(object)_underlyingDeSerializer.Deserialize(game, reader); if (!Enum.IsDefined(typeof(TEnum), value)) throw new ArgumentException( $"Invalid enum value {value} for type {typeof(TEnum)}", nameof(value)); return value; } } } public class ArrayDeSerializerGenerator : IDeSerializerGenerator { public IDeSerializer GenerateFor(Type type) { if (!type.IsArray) return null; var deSerializerType = typeof(ArrayDeSerializer<>).MakeGenericType(type.GetElementType()); return (IDeSerializer)Activator.CreateInstance(deSerializerType); } private class ArrayDeSerializer : DeSerializer { private readonly IDeSerializer _elementDeSerializer = DeSerializerRegistry.Get(true); public override void Serialize(Game game, BinaryWriter writer, T[] value) { writer.Write(value.Length); foreach (var element in value) _elementDeSerializer.Serialize(game, writer, element); } public override T[] Deserialize(Game game, BinaryReader reader) { var length = reader.ReadInt32(); var array = new T[length]; for (var i = 0; i < length; i++) array[i] = (T)_elementDeSerializer.Deserialize(game, reader); return array; } } } public class CollectionDeSerializerGenerator : IDeSerializerGenerator { public IDeSerializer GenerateFor(Type type) { Type elementType; if (type.IsInterface) { // If the type is an interface type, specific interfaces are // supported and will be populated with certain concrete types. if (!type.IsGenericType) return null; elementType = type.GetGenericArguments()[0]; var typeDef = type.GetGenericTypeDefinition(); if (typeDef == typeof(ICollection<>)) type = typeof(List<>).MakeGenericType(elementType); else if (typeDef == typeof(IList<>)) type = typeof(List<>).MakeGenericType(elementType); else if (typeDef == typeof(ISet<>)) type = typeof(HashSet<>).MakeGenericType(elementType); else return null; } else { // An empty constructor is required. if (type.GetConstructor(Type.EmptyTypes) == null) return null; // Dictionaries are handled by DictionaryDeSerializerGenerator. if (typeof(IDictionary).IsAssignableFrom(type)) return null; elementType = type.GetInterfaces() .Where(i => i.IsGenericType && (i.GetGenericTypeDefinition() == typeof(ICollection<>))) .Select(i => i.GetGenericArguments()[0]) .FirstOrDefault(); if (elementType == null) return null; } var deSerializerType = typeof(CollectionDeSerializer<,>).MakeGenericType(type, elementType); return (IDeSerializer)Activator.CreateInstance(deSerializerType); } private class CollectionDeSerializer : DeSerializer where TCollection : ICollection, new() { private readonly IDeSerializer _elementDeSerializer = DeSerializerRegistry.Get(true); public override void Serialize(Game game, BinaryWriter writer, TCollection value) { writer.Write(value.Count); foreach (var element in value) _elementDeSerializer.Serialize(game, writer, element); } public override TCollection Deserialize(Game game, BinaryReader reader) { var count = reader.ReadInt32(); var collection = new TCollection(); for (var i = 0; i < count; i++) collection.Add((TElement)_elementDeSerializer.Deserialize(game, reader)); return collection; } } } public class DictionaryDeSerializerGenerator : IDeSerializerGenerator { public IDeSerializer GenerateFor(Type type) { Type keyType, valueType; if (type.IsInterface) { if (!type.IsGenericType || (type.GetGenericTypeDefinition() != typeof(IDictionary<,>))) return null; keyType = type.GetGenericArguments()[0]; valueType = type.GetGenericArguments()[1]; type = typeof(Dictionary<,>).MakeGenericType(keyType, valueType); } else { // An empty constructor is required. if (type.GetConstructor(Type.EmptyTypes) == null) return null; (keyType, valueType) = type.GetInterfaces() .Where(i => i.IsGenericType && (i.GetGenericTypeDefinition() == typeof(IDictionary<,>))) .Select(i => (i.GetGenericArguments()[0], i.GetGenericArguments()[1])) .FirstOrDefault(); if (keyType == null) return null; } var deSerializerType = typeof(DictionaryDeSerializer<,,>).MakeGenericType(type, keyType, valueType); return (IDeSerializer)Activator.CreateInstance(deSerializerType); } private class DictionaryDeSerializer : DeSerializer where TDictionary : IDictionary, new() { private readonly IDeSerializer _keyDeSerializer = DeSerializerRegistry.Get(true); private readonly IDeSerializer _valueDeSerializer = DeSerializerRegistry.Get(true); public override void Serialize(Game game, BinaryWriter writer, TDictionary dict) { writer.Write(dict.Count); foreach (var (key, value) in dict) { _keyDeSerializer.Serialize(game, writer, key); _valueDeSerializer.Serialize(game, writer, value); } } public override TDictionary Deserialize(Game game, BinaryReader reader) { var count = reader.ReadInt32(); var dictionary = new TDictionary(); for (var i = 0; i < count; i++) dictionary.Add((TKey)_keyDeSerializer.Deserialize(game, reader), (TValue)_valueDeSerializer.Deserialize(game, reader)); return dictionary; } } } public class SyncedObjectDeSerializerGenerator : IDeSerializerGenerator { public IDeSerializer GenerateFor(Type type) { if (!typeof(Node).IsAssignableFrom(type) || (type.GetCustomAttribute() == null)) return null; var deSerializerType = typeof(SyncedObjectDeSerializer<>).MakeGenericType(type); return (IDeSerializer)Activator.CreateInstance(deSerializerType); } private class SyncedObjectDeSerializer : DeSerializer where TObj : Node { public override void Serialize(Game game, BinaryWriter writer, TObj value) => writer.Write(game.Sync.GetStatusOrThrow(value).SyncID); public override TObj Deserialize(Game game, BinaryReader reader) { var id = reader.ReadUInt32(); var value = (TObj)game.Sync.GetStatusOrThrow(id).Object; if (value == null) throw new Exception($"Could not find synced object of type {typeof(TObj)} with ID {id}"); return value; } } } // TODO: Replace this with something that will generate code at runtime for improved performance. public class ComplexDeSerializer : IDeSerializer { private readonly Type _type; private event Action OnSerialize; private event Action OnDeserialize; public ComplexDeSerializer(Type type) { _type = type; foreach (var field in type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)) { var deSerializer = DeSerializerRegistry.Get(field.FieldType, true); OnSerialize += (game, writer, value) => deSerializer.Serialize(game, writer, field.GetValue(value)); OnDeserialize += (game, reader, instance) => field.SetValue(instance, deSerializer.Deserialize(game, reader)); } if (OnSerialize == null) throw new InvalidOperationException( $"Unable to create serializer for type {type}"); } public void Serialize(Game game, BinaryWriter writer, object value) => OnSerialize(game, writer, value); public object Deserialize(Game game, BinaryReader reader) { var instance = FormatterServices.GetUninitializedObject(_type); OnDeserialize(game, reader, instance); return instance; } }