diff --git a/Reactor/Networking/Attributes/MessageConverterAttribute.cs b/Reactor/Networking/Attributes/MessageConverterAttribute.cs index e3515be..3efd3b9 100644 --- a/Reactor/Networking/Attributes/MessageConverterAttribute.cs +++ b/Reactor/Networking/Attributes/MessageConverterAttribute.cs @@ -38,8 +38,7 @@ public static void Register(Assembly assembly) try { - var messageConverter = (UnsafeMessageConverter) Activator.CreateInstance(type)!; - MessageSerializer.Register(messageConverter); + MessageSerializer.Register(type); } catch (Exception e) { @@ -52,5 +51,6 @@ public static void Register(Assembly assembly) internal static void Initialize() { IL2CPPChainloader.Instance.PluginLoad += (_, assembly, _) => Register(assembly); + IL2CPPChainloader.Instance.Finished += MessageSerializer.ClearMaps; } } diff --git a/Reactor/Networking/Extensions/ExtraMessageExtensions.cs b/Reactor/Networking/Extensions/ExtraMessageExtensions.cs index f124fa0..439cf42 100644 --- a/Reactor/Networking/Extensions/ExtraMessageExtensions.cs +++ b/Reactor/Networking/Extensions/ExtraMessageExtensions.cs @@ -1,6 +1,8 @@ using System; +using System.Globalization; using Hazel; using Hazel.Udp; +using Reactor.Networking.Serialization; using UnityEngine; namespace Reactor.Networking.Extensions; @@ -12,10 +14,11 @@ public static class ExtraMessageExtensions { private const float MIN = -50f; private const float MAX = 50f; + private const float DIFF = MAX - MIN; private static float ReverseLerp(float t) { - return Mathf.Clamp((t - MIN) / (MAX - MIN), 0f, 1f); + return Mathf.Clamp01((t - MIN) / DIFF); } /// @@ -32,6 +35,33 @@ public static void Write(this MessageWriter writer, Vector2 value) writer.Write(y); } + /// + /// Writes an Enum value to the . + /// + /// The to write to. + /// The to write. + public static void Write(this MessageWriter writer, Enum value) => writer.Serialize(Convert.ChangeType(value, Enum.GetUnderlyingType(value.GetType()), CultureInfo.InvariantCulture)); + + /// + /// Writes a long value to the . + /// + /// The to write to. + /// The to write. + public static void Write(this MessageWriter writer, long value) => writer.Write(BitConverter.GetBytes(value)); + + /// + /// Writes a color value to the . + /// + /// The to write to. + /// The to write. + public static void Write(this MessageWriter writer, Color32 value) + { + writer.Write(value.r); + writer.Write(value.g); + writer.Write(value.b); + writer.Write(value.a); + } + /// /// Reads a from the . /// @@ -45,6 +75,43 @@ public static Vector2 ReadVector2(this MessageReader reader) return new Vector2(Mathf.Lerp(MIN, MAX, x), Mathf.Lerp(MIN, MAX, y)); } + /// + /// Reads an enum value and casts it to the specified type from a network message. + /// + /// The to read from. + /// The type to convert to. + /// An value from the . + public static T ReadEnum(this MessageReader reader) where T : struct, Enum => (T) reader.ReadEnum(typeof(T)); + + /// + /// Reads an enum value from a network message. + /// + /// The to read from. + /// The type of the enum. + /// The resulting enum value from the . + public static object ReadEnum(this MessageReader reader, Type enumType) => Enum.ToObject(enumType, reader.Deserialize(Enum.GetUnderlyingType(enumType))); + + /// + /// Reads a long value from a network message. + /// + /// The to read from. + /// The resulting long value from the . + public static long ReadInt64(this MessageReader reader) => BitConverter.ToInt64(reader.ReadBytes(8), 0); + + /// + /// Reads a color value from a network message. + /// + /// The to read from. + /// The resulting Color32 value from the . + public static Color32 ReadColor32(this MessageReader reader) => new(reader.ReadByte(), reader.ReadByte(), reader.ReadByte(), reader.ReadByte()); + + /// + /// Reads a color value from a network message. + /// + /// The to read from. + /// The resulting Color value from the . + public static Color ReadColor(this MessageReader reader) => new(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle()); + /// /// Sends a message on the with an . /// diff --git a/Reactor/Networking/Serialization/MessageSerializer.cs b/Reactor/Networking/Serialization/MessageSerializer.cs index 3f92591..5a1236c 100644 --- a/Reactor/Networking/Serialization/MessageSerializer.cs +++ b/Reactor/Networking/Serialization/MessageSerializer.cs @@ -15,33 +15,83 @@ public static class MessageSerializer private static List MessageConverters { get; } = new(); private static Dictionary MessageConverterMap { get; } = new(); + private static Dictionary GenericConvertersMap { get; } = new(); + + internal static void ClearMaps() + { + MessageConverterMap.Clear(); + } /// /// Registers a MessageConverter. /// - /// The MessageConverter to be registered. - public static void Register(UnsafeMessageConverter messageConverter) + /// The Type of the MessageConverter to be registered. + public static void Register(Type type) { - MessageConverters.Add(messageConverter); - MessageConverterMap.Clear(); + if (type.IsGenericTypeDefinition) + { + var baseType = type.BaseType!; + + if (!baseType.Name.Contains("MessageConverter")) + throw new InvalidOperationException($"{type.Name} should directly inherit from MessageConverter"); + + var generics = baseType.GetGenericArguments(); + var param = generics[0]; + + GenericConvertersMap.Add(param.GetGenericTypeDefinition(), type); + } + else + { + var messageConverter = (UnsafeMessageConverter) Activator.CreateInstance(type)!; + MessageConverters.Add(messageConverter); + } } /// /// Finds a MessageConverter for the specified . /// /// The type of an object. - /// A MessageConverted that can convert the specified . + /// A MessageConverter that can convert the specified . public static UnsafeMessageConverter? FindConverter(Type type) { - if (MessageConverterMap.TryGetValue(type, out var value)) + if (!MessageConverterMap.TryGetValue(type, out var value)) { - return value; + value = MessageConverters.SingleOrDefault(x => x.CanConvert(type)); + + if (value == null) + return null; + + MessageConverterMap.Add(type, value); } - var converter = MessageConverters.SingleOrDefault(x => x.CanConvert(type)); - MessageConverterMap.Add(type, converter); + return value; + } - return converter; + /// + /// Finds and builds a MessageConverter for the specified using a registered generic converter. + /// + /// The type of an object. + /// A MessageConverter that can convert the specified . + public static UnsafeMessageConverter? FindGenericConverter(Type type) + { + if (MessageConverterMap.TryGetValue(type, out var value)) + return value; + + if (!type.IsGenericType) + return null; + + var typeDef = type.GetGenericTypeDefinition(); + + if (!GenericConvertersMap.TryGetValue(typeDef, out var builder)) + return null; + + var generic = builder.MakeGenericType(type.GetGenericArguments()); + value = (UnsafeMessageConverter) Activator.CreateInstance(generic)!; + + MessageConverters.Add(value); + MessageConverterMap.Add(type, value); + + return value; } /// @@ -49,7 +99,7 @@ public static void Register(UnsafeMessageConverter messageConverter) /// /// The to write to. /// The args to be written. - public static void Serialize(MessageWriter writer, object[] args) + public static void Serialize(this MessageWriter writer, params object[] args) { foreach (var arg in args) { @@ -87,24 +137,48 @@ public static void Serialize(this MessageWriter writer, object @object) case bool i: writer.Write(i); break; + case ulong i: + writer.Write(i); + break; + case long i: + ExtraMessageExtensions.Write(writer, i); // For some reason this insists on referring the to float write method, so this is me taking precautions + break; case Vector2 i: writer.Write(i); break; case string i: writer.Write(i); break; + case Color i: + writer.Write(i); + break; + case Color32 i: + writer.Write(i); + break; + case Enum i: + writer.Write(i); + break; default: - var converter = FindConverter(@object.GetType()); + var type = @object.GetType(); + var converter = FindGenericConverter(type) ?? FindConverter(type); + if (converter != null) - { converter.UnsafeWrite(writer, @object); - break; - } + else + throw new NotSupportedException("Couldn't serialize " + type.Name); - throw new NotSupportedException("Couldn't serialize " + @object.GetType()); + break; } } + /// + /// Deserializes a generic value from the . + /// + /// The to read from. + /// The type to be read. + /// A generic value from the . + public static T Deserialize(this MessageReader reader) => (T) reader.Deserialize(typeof(T)); + /// /// Deserializes an of from the . /// @@ -158,7 +232,33 @@ public static object Deserialize(this MessageReader reader, Type objectType) return reader.ReadString(); } - var converter = FindConverter(objectType); + if (objectType == typeof(ulong)) + { + return reader.ReadUInt64(); + } + + if (objectType == typeof(long)) + { + return reader.ReadInt64(); + } + + if (objectType == typeof(Color)) + { + return reader.ReadColor(); + } + + if (objectType == typeof(Color32)) + { + return reader.ReadColor32(); + } + + if (objectType.IsEnum) + { + return reader.ReadEnum(objectType); + } + + var converter = FindGenericConverter(objectType) ?? FindConverter(objectType); + if (converter != null) { return converter.UnsafeRead(reader, objectType);