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 . /// [Obsolete("Lidgren.Network support is deprecated. Consider using LiteNetLib or a custom implementation instead.")] #if NET6_0_OR_GREATER [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Aot", "IL2070")] #endif 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)); } } }