Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Reactor/Networking/Attributes/MessageConverterAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand All @@ -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;
}
}
69 changes: 68 additions & 1 deletion Reactor/Networking/Extensions/ExtraMessageExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using System;
using System.Globalization;
using Hazel;
using Hazel.Udp;
using Reactor.Networking.Serialization;
using UnityEngine;

namespace Reactor.Networking.Extensions;
Expand All @@ -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);
}

/// <summary>
Expand All @@ -32,6 +35,33 @@ public static void Write(this MessageWriter writer, Vector2 value)
writer.Write(y);
}

/// <summary>
/// Writes an Enum value to the <paramref name="writer"/>.
/// </summary>
/// <param name="writer">The <see cref="MessageWriter"/> to write to.</param>
/// <param name="value">The <see cref="Enum"/> to write.</param>
public static void Write(this MessageWriter writer, Enum value) => writer.Serialize(Convert.ChangeType(value, Enum.GetUnderlyingType(value.GetType()), CultureInfo.InvariantCulture));

/// <summary>
/// Writes a long value to the <paramref name="writer"/>.
/// </summary>
/// <param name="writer">The <see cref="MessageWriter"/> to write to.</param>
/// <param name="value">The <see cref="long"/> to write.</param>
public static void Write(this MessageWriter writer, long value) => writer.Write(BitConverter.GetBytes(value));

/// <summary>
/// Writes a color value to the <paramref name="writer"/>.
/// </summary>
/// <param name="writer">The <see cref="MessageWriter"/> to write to.</param>
/// <param name="value">The <see cref="Color32"/> to write.</param>
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);
}

/// <summary>
/// Reads a <see cref="Vector2"/> from the <paramref name="reader"/>.
/// </summary>
Expand All @@ -45,6 +75,43 @@ public static Vector2 ReadVector2(this MessageReader reader)
return new Vector2(Mathf.Lerp(MIN, MAX, x), Mathf.Lerp(MIN, MAX, y));
}

/// <summary>
/// Reads an enum value and casts it to the specified type from a network message.
/// </summary>
/// <param name="reader">The <see cref="MessageReader"/> to read from.</param>
/// <typeparam name="T">The <see cref="Enum"/> type to convert to.</typeparam>
/// <returns>An <see cref="Enum"/> value from the <paramref name="reader"/>.</returns>
public static T ReadEnum<T>(this MessageReader reader) where T : struct, Enum => (T) reader.ReadEnum(typeof(T));

/// <summary>
/// Reads an enum value from a network message.
/// </summary>
/// <param name="reader">The <see cref="MessageReader"/> to read from.</param>
/// <param name="enumType">The type of the enum.</param>
/// <returns>The resulting enum value from the <paramref name="reader"/>.</returns>
public static object ReadEnum(this MessageReader reader, Type enumType) => Enum.ToObject(enumType, reader.Deserialize(Enum.GetUnderlyingType(enumType)));

/// <summary>
/// Reads a long value from a network message.
/// </summary>
/// <param name="reader">The <see cref="MessageReader"/> to read from.</param>
/// <returns>The resulting long value from the <paramref name="reader"/>.</returns>
public static long ReadInt64(this MessageReader reader) => BitConverter.ToInt64(reader.ReadBytes(8), 0);

/// <summary>
/// Reads a color value from a network message.
/// </summary>
/// <param name="reader">The <see cref="MessageReader"/> to read from.</param>
/// <returns>The resulting Color32 value from the <paramref name="reader"/>.</returns>
public static Color32 ReadColor32(this MessageReader reader) => new(reader.ReadByte(), reader.ReadByte(), reader.ReadByte(), reader.ReadByte());

/// <summary>
/// Reads a color value from a network message.
/// </summary>
/// <param name="reader">The <see cref="MessageReader"/> to read from.</param>
/// <returns>The resulting Color value from the <paramref name="reader"/>.</returns>
public static Color ReadColor(this MessageReader reader) => new(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());

/// <summary>
/// Sends a message on the <paramref name="connection"/> with an <paramref name="ackCallback"/>.
/// </summary>
Expand Down
134 changes: 117 additions & 17 deletions Reactor/Networking/Serialization/MessageSerializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,41 +15,91 @@ public static class MessageSerializer
private static List<UnsafeMessageConverter> MessageConverters { get; } = new();

private static Dictionary<Type, UnsafeMessageConverter?> MessageConverterMap { get; } = new();
private static Dictionary<Type, Type> GenericConvertersMap { get; } = new();

internal static void ClearMaps()
{
MessageConverterMap.Clear();
}

/// <summary>
/// Registers a MessageConverter.
/// </summary>
/// <param name="messageConverter">The MessageConverter to be registered.</param>
public static void Register(UnsafeMessageConverter messageConverter)
/// <param name="type">The Type of the MessageConverter to be registered.</param>
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<T>");

var generics = baseType.GetGenericArguments();
var param = generics[0];

GenericConvertersMap.Add(param.GetGenericTypeDefinition(), type);
}
else
{
var messageConverter = (UnsafeMessageConverter) Activator.CreateInstance(type)!;
MessageConverters.Add(messageConverter);
}
}

/// <summary>
/// Finds a MessageConverter for the specified <paramref name="type"/>.
/// </summary>
/// <param name="type">The type of an object.</param>
/// <returns>A MessageConverted that can convert the specified <see cref="Type"/>.</returns>
/// <returns>A MessageConverter that can convert the specified <see cref="Type"/>.</returns>
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;
/// <summary>
/// Finds and builds a MessageConverter for the specified <paramref name="type"/> using a registered generic converter.
/// </summary>
/// <param name="type">The type of an object.</param>
/// <returns>A MessageConverter that can convert the specified <see cref="Type"/>.</returns>
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;
}

/// <summary>
/// Serializes <paramref name="args"/> to the <paramref name="writer"/>.
/// </summary>
/// <param name="writer">The <see cref="MessageWriter"/> to write to.</param>
/// <param name="args">The args to be written.</param>
public static void Serialize(MessageWriter writer, object[] args)
public static void Serialize(this MessageWriter writer, params object[] args)
{
foreach (var arg in args)
{
Expand Down Expand Up @@ -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;
}
}

/// <summary>
/// Deserializes a generic <typeparamref name="T"/> value from the <paramref name="reader"/>.
/// </summary>
/// <param name="reader">The <see cref="MessageReader"/> to read from.</param>
/// <typeparam name="T">The type to be read.</typeparam>
/// <returns>A generic <typeparamref name="T"/> value from the <paramref name="reader"/>.</returns>
public static T Deserialize<T>(this MessageReader reader) => (T) reader.Deserialize(typeof(T));

/// <summary>
/// Deserializes an <see cref="object"/> of <paramref name="objectType"/> from the <paramref name="reader"/>.
/// </summary>
Expand Down Expand Up @@ -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);
Expand Down