using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using MLAPI.Reflection;
using UnityEngine;
namespace MLAPI.Serialization
{
///
/// Helper class to manage the MLAPI serialization.
///
public static class SerializationManager
{
private static Dictionary s_FieldCache = new Dictionary();
private static Dictionary s_CachedExternalSerializers = new Dictionary();
private static Dictionary s_CachedExternalDeserializers = new Dictionary();
///
/// The delegate used when registering custom deserialization for a type.
///
/// The stream to read the data required to construct the type.
/// The type to deserialize.
public delegate T CustomDeserializationDelegate(Stream stream);
///
/// The delegate used when registering custom serialization for a type.
///
/// The stream to write data to that is required to reconstruct the type in the deserialization delegate.
/// The instance to serialize to the stream.
/// The type to serialize.
public delegate void CustomSerializationDelegate(Stream stream, T instance);
// These two are what we use internally. They box the value.
private delegate void BoxedSerializationDelegate(Stream stream, object instance);
private delegate object BoxedDeserializationDelegate(Stream stream);
///
/// Registers a custom serialization and deserialization pair for a object.
/// This is useful for writing objects that are behind the third party wall. Such as .NET types.
///
/// The delegate to invoke to serialize the type.
/// The delegate to invoke to deserialize the type.
/// The type to register.
public static void RegisterSerializationHandlers(CustomSerializationDelegate onSerialize, CustomDeserializationDelegate onDeserialize)
{
s_CachedExternalSerializers[typeof(T)] = (stream, instance) => onSerialize(stream, (T)instance);
s_CachedExternalDeserializers[typeof(T)] = stream => onDeserialize(stream);
}
///
/// Removes a serialization handler that was registered previously for a specific type.
/// This will remove both the serialization and deserialization handler.
///
/// The type for the serialization handlers to remove.
/// Whether or not either the serialization or deserialization handlers for the type was removed.
public static bool RemoveSerializationHandlers()
{
bool serializationRemoval = s_CachedExternalSerializers.Remove(typeof(T));
bool deserializationRemoval = s_CachedExternalDeserializers.Remove(typeof(T));
return serializationRemoval || deserializationRemoval;
}
internal static bool TrySerialize(Stream stream, object obj)
{
if (s_CachedExternalSerializers.ContainsKey(obj.GetType()))
{
s_CachedExternalSerializers[obj.GetType()](stream, obj);
return true;
}
return false;
}
internal static bool TryDeserialize(Stream stream, Type type, out object obj)
{
if (s_CachedExternalDeserializers.ContainsKey(type))
{
obj = s_CachedExternalDeserializers[type](stream);
return true;
}
obj = null;
return false;
}
internal static FieldInfo[] GetFieldsForType(Type type)
{
if (s_FieldCache.ContainsKey(type)) return s_FieldCache[type];
FieldInfo[] fields = type
.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.Where(x => (x.IsPublic || x.GetCustomAttributes(typeof(SerializeField), true).Length > 0) && IsTypeSupported(x.FieldType))
.OrderBy(x => x.Name, StringComparer.Ordinal).ToArray();
s_FieldCache.Add(type, fields);
return fields;
}
private static HashSet s_SupportedTypes = new HashSet()
{
typeof(byte),
typeof(byte),
typeof(sbyte),
typeof(ushort),
typeof(short),
typeof(int),
typeof(uint),
typeof(long),
typeof(ulong),
typeof(float),
typeof(double),
typeof(string),
typeof(bool),
typeof(Vector2),
typeof(Vector3),
typeof(Vector4),
typeof(Color),
typeof(Color32),
typeof(Ray),
typeof(Quaternion),
typeof(char),
typeof(GameObject),
typeof(NetworkObject),
typeof(NetworkBehaviour)
};
///
/// Returns if a type is supported for serialization
///
/// The type to check
/// Whether or not the type is supported
public static bool IsTypeSupported(Type type)
{
return type.IsEnum || s_SupportedTypes.Contains(type) || type.HasInterface(typeof(INetworkSerializable)) ||
(s_CachedExternalSerializers.ContainsKey(type) && s_CachedExternalDeserializers.ContainsKey(type)) ||
(type.IsArray && type.HasElementType && IsTypeSupported(type.GetElementType()));
}
}
}