- Allow registering de/serializer "generators" - Add generators for arrays, lists and dictionaries - Move de/serialization code into extra file (still part of NetworkAPI thanks to "partial") - Synchronize existing blocks to new playersmain
parent
4f0d5a537e
commit
9b53e6b2d3
5 changed files with 280 additions and 98 deletions
@ -0,0 +1,198 @@ |
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.IO; |
||||
using System.Linq; |
||||
using System.Reflection; |
||||
using System.Runtime.Serialization; |
||||
|
||||
public interface INetworkDeSerializer |
||||
{ |
||||
void Serialize(BinaryWriter writer, object value); |
||||
object Deserialize(BinaryReader reader); |
||||
} |
||||
|
||||
public interface INetworkDeSerializerGenerator |
||||
{ |
||||
INetworkDeSerializer GenerateFor(Type type); |
||||
} |
||||
|
||||
public partial class NetworkAPI |
||||
{ |
||||
private class SimpleDeSerializer<T> |
||||
: INetworkDeSerializer |
||||
{ |
||||
private readonly Action<BinaryWriter, T> _serialize; |
||||
private readonly Func<BinaryReader, T> _deserialize; |
||||
public SimpleDeSerializer(Action<BinaryWriter, T> serialize, Func<BinaryReader, T> deserialize) |
||||
{ _serialize = serialize; _deserialize = deserialize; } |
||||
public void Serialize(BinaryWriter writer, object value) => _serialize(writer, (T)value); |
||||
public object Deserialize(BinaryReader reader) => _deserialize(reader); |
||||
} |
||||
|
||||
private class ArrayDeSerializerGenerator |
||||
: INetworkDeSerializerGenerator |
||||
{ |
||||
public INetworkDeSerializer GenerateFor(Type type) |
||||
{ |
||||
if (!type.IsArray) return null; |
||||
var deSerializerType = typeof(ArrayDeSerializer<>).MakeGenericType(type.GetElementType()); |
||||
return (INetworkDeSerializer)Activator.CreateInstance(deSerializerType); |
||||
} |
||||
} |
||||
private class ArrayDeSerializer<T> |
||||
: INetworkDeSerializer |
||||
{ |
||||
private readonly INetworkDeSerializer _elementDeSerializer = |
||||
Network.API.GetDeSerializer(typeof(T), true); |
||||
|
||||
public void Serialize(BinaryWriter writer, object value) |
||||
{ |
||||
var array = (T[])value; |
||||
writer.Write(array.Length); |
||||
foreach (var element in array) _elementDeSerializer.Serialize(writer, element); |
||||
} |
||||
|
||||
public object Deserialize(BinaryReader reader) |
||||
{ |
||||
var length = reader.ReadInt32(); |
||||
var array = new T[length]; |
||||
for (var i = 0; i < length; i++) |
||||
array[i] = (T)_elementDeSerializer.Deserialize(reader); |
||||
return array; |
||||
} |
||||
} |
||||
|
||||
private class CollectionDeSerializerGenerator |
||||
: INetworkDeSerializerGenerator |
||||
{ |
||||
public INetworkDeSerializer GenerateFor(Type type) |
||||
{ |
||||
Type elementType; |
||||
if (type.IsInterface) { |
||||
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 { |
||||
if (type.GetConstructor(Type.EmptyTypes) == null) 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 (INetworkDeSerializer)Activator.CreateInstance(deSerializerType); |
||||
} |
||||
} |
||||
private class CollectionDeSerializer<TCollection, TElement> |
||||
: INetworkDeSerializer |
||||
where TCollection : ICollection<TElement>, new() |
||||
{ |
||||
private readonly INetworkDeSerializer _elementDeSerializer = |
||||
Network.API.GetDeSerializer(typeof(TElement), true); |
||||
|
||||
public void Serialize(BinaryWriter writer, object value) |
||||
{ |
||||
var collection = (TCollection)value; |
||||
writer.Write(collection.Count); |
||||
foreach (var element in collection) |
||||
_elementDeSerializer.Serialize(writer, element); |
||||
} |
||||
|
||||
public object Deserialize(BinaryReader reader) |
||||
{ |
||||
var count = reader.ReadInt32(); |
||||
var collection = new TCollection(); |
||||
for (var i = 0; i < count; i++) |
||||
collection.Add((TElement)_elementDeSerializer.Deserialize(reader)); |
||||
return collection; |
||||
} |
||||
} |
||||
|
||||
private class DictionaryDeSerializerGenerator |
||||
: INetworkDeSerializerGenerator |
||||
{ |
||||
public INetworkDeSerializer 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 { |
||||
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 (INetworkDeSerializer)Activator.CreateInstance(deSerializerType); |
||||
} |
||||
} |
||||
private class DictionaryDeSerializer<TDictionary, TKey, TValue> |
||||
: INetworkDeSerializer |
||||
where TDictionary : IDictionary<TKey, TValue>, new() |
||||
{ |
||||
private readonly INetworkDeSerializer _keyDeSerializer = |
||||
Network.API.GetDeSerializer(typeof(TKey), true); |
||||
private readonly INetworkDeSerializer _valueDeSerializer = |
||||
Network.API.GetDeSerializer(typeof(TKey), true); |
||||
|
||||
public void Serialize(BinaryWriter writer, object value) |
||||
{ |
||||
var dictionary = (TDictionary)value; |
||||
writer.Write(dictionary.Count); |
||||
foreach (var element in dictionary) { |
||||
_keyDeSerializer.Serialize(writer, element.Key); |
||||
_valueDeSerializer.Serialize(writer, element.Value); |
||||
} |
||||
} |
||||
|
||||
public object Deserialize(BinaryReader reader) |
||||
{ |
||||
var count = reader.ReadInt32(); |
||||
var dictionary = new TDictionary(); |
||||
for (var i = 0; i < count; i++) |
||||
dictionary.Add((TKey)_keyDeSerializer.Deserialize(reader), |
||||
(TValue)_valueDeSerializer.Deserialize(reader)); |
||||
return dictionary; |
||||
} |
||||
} |
||||
|
||||
// TODO: Replace this with something that will generate code at runtime for improved performance. |
||||
private class ComplexDeSerializer |
||||
: INetworkDeSerializer |
||||
{ |
||||
private readonly Type _type; |
||||
private event Action<BinaryWriter, object> OnSerialize; |
||||
private event Action<BinaryReader, object> OnDeserialize; |
||||
|
||||
public ComplexDeSerializer(Type type) |
||||
{ |
||||
_type = type; |
||||
foreach (var field in type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)) { |
||||
var deSerializer = Network.API.GetDeSerializer(field.FieldType, true); |
||||
OnSerialize += (writer, value) => deSerializer.Serialize(writer, field.GetValue(value)); |
||||
OnDeserialize += (reader, instance) => field.SetValue(instance, deSerializer.Deserialize(reader)); |
||||
} |
||||
if (OnSerialize == null) throw new InvalidOperationException( |
||||
$"Unable to create serializer for type {type}"); |
||||
} |
||||
|
||||
public void Serialize(BinaryWriter writer, object value) |
||||
=> OnSerialize(writer, value); |
||||
public object Deserialize(BinaryReader reader) |
||||
{ |
||||
var instance = FormatterServices.GetUninitializedObject(_type); |
||||
OnDeserialize(reader, instance); |
||||
return instance; |
||||
} |
||||
} |
||||
} |
Loading…
Reference in new issue