using System;
using System.Collections.Generic;
using System.Reflection;
using Lidgren.Network;
using Newtonsoft.Json;
namespace MLEM.Data {
///
/// A net buffer serializer allows easily writing generic objects into a Lidgren.Network .
/// It can be used both for serialization of outgoing packets, and deserialization of incoming packets.
/// Before serializing and deserializing an object, each of the object's fields has to have a handler. New handlers can be added using or .
///
public class NetBufferSerializer {
private readonly Dictionary> writeFunctions = new Dictionary>();
private readonly Dictionary> readFunctions = new Dictionary>();
private readonly Dictionary fieldCache = new Dictionary();
///
/// Create a new net buffer serializer with some default serialization and deserialization implementations for various types.
///
public NetBufferSerializer() {
foreach (var method in typeof(NetBuffer).GetMethods(BindingFlags.Instance | BindingFlags.Public)) {
if (method.GetParameters().Length == 0 && method.Name.StartsWith("Read", StringComparison.Ordinal) && method.Name.Substring(4) == method.ReturnType.Name)
this.readFunctions[method.ReturnType] = buffer => method.Invoke(buffer, null);
}
foreach (var method in typeof(NetBuffer).GetMethods(BindingFlags.Instance | BindingFlags.Public)) {
if (method.Name.Equals("Write", StringComparison.InvariantCulture)) {
var parameters = method.GetParameters();
if (parameters.Length == 1)
this.writeFunctions[parameters[0].ParameterType] = (buffer, o) => method.Invoke(buffer, new[] {o});
}
}
this.AddHandler((buffer, o) => buffer.Write(o), buffer => buffer.ReadVector2());
this.AddHandler((buffer, o) => buffer.Write(o), buffer => buffer.ReadGuid());
this.AddHandler((buffer, o) => buffer.Write(o), buffer => buffer.ReadDirection());
}
///
/// Serializes the given object into the given net buffer.
/// Note that each field in the object has to have a handler ()
///
/// The buffer to serialize into
/// The object to serialize
/// The binding flags to search for fields in the object by
/// If any of the object's fields has no writer
public void Serialize(NetBuffer buffer, object o, BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) {
foreach (var field in this.GetFields(o.GetType(), flags)) {
if (!this.writeFunctions.TryGetValue(field.FieldType, out var func))
throw new ArgumentException($"The type {field.FieldType} doesn't have a writer");
func(buffer, field.GetValue(o));
}
}
///
/// Deserializes the net buffer's content into the given object.
/// If this is used for packet serialization, a new instance of the required type has to be created before this method is called.
///
/// The buffer to read the data from
/// The object to serialize into
/// The binding flags to search for fields in the object by
/// If any of the object's fields has no reader
public void Deserialize(NetBuffer buffer, object o, BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) {
foreach (var field in this.GetFields(o.GetType(), flags)) {
if (!this.readFunctions.TryGetValue(field.FieldType, out var func))
throw new ArgumentException($"The type {field.FieldType} doesn't have a reader");
field.SetValue(o, func(buffer));
}
}
private IEnumerable GetFields(Type type, BindingFlags flags) {
if (!this.fieldCache.TryGetValue(type, out var fields)) {
fields = type.GetFields(flags);
Array.Sort(fields, (f1, f2) => string.Compare(f1.Name, f2.Name, StringComparison.Ordinal));
this.fieldCache.Add(type, fields);
}
return fields;
}
///
/// Adds a manually created deserialization and serialization handler to this net buffer serializer.
///
/// The function to write the given object into the net buffer
/// The function to read the given object out of the net buffer
/// The type that will be serialized and deserialized
public void AddHandler(Action write, Func read) {
this.writeFunctions.Add(typeof(T), (buffer, o) => write(buffer, (T) o));
this.readFunctions.Add(typeof(T), buffer => read(buffer));
}
///
/// Adds a JSON-based deserialization and serialization handler to this net buffer serializer.
/// Objects that are serialized in this way are converted to JSON, and the resulting JSON is compressed.
///
/// The JSON serializer to use
/// The type that will be serialized and deserialized
public void AddHandler(JsonSerializer serializer) {
this.AddHandler((buffer, o) => buffer.WriteObject(o, serializer), buffer => buffer.ReadObject(serializer));
}
}
}