Files
ShrinkNetwork/Runtime/Serialization/ShrinkMessagePackNetworkSerializer.cs
T
cneicy 8eaaa3040a
Publish UPM package / publish (push) Failing after 1s
chore: initialize standalone UPM package
2026-08-26 02:50:34 +08:00

151 lines
6.1 KiB
C#

#nullable enable
using System;
using System.Linq;
using System.Reflection;
namespace ShrinkNetwork
{
public sealed class ShrinkMessagePackNetworkSerializer : IShrinkNetworkSerializer
{
private readonly MethodInfo _serializeMethod;
private readonly MethodInfo _deserializeMethod;
private readonly object? _serializerOptions;
public ShrinkMessagePackNetworkSerializer()
{
var serializerType = Type.GetType("MessagePack.MessagePackSerializer, MessagePack");
if (serializerType == null)
{
throw new InvalidOperationException(
"MessagePack assembly was not found. Please install MessagePack-CSharp before using ShrinkMessagePackNetworkSerializer.");
}
_serializerOptions = ResolveSerializerOptions(serializerType.Assembly);
var serializeMethod = serializerType
.GetMethods(BindingFlags.Public | BindingFlags.Static)
.FirstOrDefault(m =>
{
if (m.Name != "Serialize")
return false;
var parameters = m.GetParameters();
return parameters.Length >= 2 &&
parameters[0].ParameterType == typeof(Type) &&
parameters[1].ParameterType == typeof(object);
});
var deserializeMethod = serializerType
.GetMethods(BindingFlags.Public | BindingFlags.Static)
.FirstOrDefault(m =>
{
if (m.Name != "Deserialize")
return false;
var parameters = m.GetParameters();
return parameters.Length >= 2 &&
parameters[0].ParameterType == typeof(Type) &&
(parameters[1].ParameterType == typeof(byte[]) ||
parameters[1].ParameterType == typeof(ReadOnlyMemory<byte>));
});
if (serializeMethod == null || deserializeMethod == null)
throw new MissingMethodException("MessagePack serialize/deserialize API not found.");
_serializeMethod = serializeMethod;
_deserializeMethod = deserializeMethod;
}
public byte[] Serialize(object value)
{
if (value == null)
return Array.Empty<byte>();
var parameters = BuildParameters(_serializeMethod, value.GetType(), value, _serializerOptions);
return (byte[])_serializeMethod.Invoke(null, parameters)!;
}
public object Deserialize(byte[] payload, Type type)
{
payload ??= Array.Empty<byte>();
var parameters = BuildParameters(_deserializeMethod, type, payload, _serializerOptions);
return _deserializeMethod.Invoke(null, parameters)
?? throw new InvalidOperationException($"MessagePack returned null for type {type.FullName}.");
}
public T Deserialize<T>(byte[] payload)
{
return (T)Deserialize(payload, typeof(T));
}
private static object?[] BuildParameters(MethodInfo method, Type type, object valueOrBytes, object? serializerOptions)
{
var parameters = method.GetParameters();
var args = new object?[parameters.Length];
if (parameters.Length > 0)
args[0] = type;
if (parameters.Length > 1)
args[1] = ConvertPrimaryArgument(parameters[1].ParameterType, valueOrBytes);
for (var i = 2; i < parameters.Length; i++)
{
args[i] = ResolveAdditionalArgument(parameters[i], serializerOptions);
}
return args;
}
private static object? ResolveAdditionalArgument(ParameterInfo parameter, object? serializerOptions)
{
if (serializerOptions != null && parameter.ParameterType.IsInstanceOfType(serializerOptions))
return serializerOptions;
return parameter.HasDefaultValue
? parameter.DefaultValue
: GetDefault(parameter.ParameterType);
}
private static object ConvertPrimaryArgument(Type parameterType, object value)
{
if (parameterType == typeof(ReadOnlyMemory<byte>) && value is byte[] bytes)
return new ReadOnlyMemory<byte>(bytes);
return value;
}
private static object? ResolveSerializerOptions(Assembly serializerAssembly)
{
var contractlessResolverType = serializerAssembly.GetType("MessagePack.Resolvers.ContractlessStandardResolver");
if (contractlessResolverType != null)
{
var optionsField = contractlessResolverType.GetField("Options",
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static);
var options = optionsField?.GetValue(null);
if (options != null)
return options;
var instanceField = contractlessResolverType.GetField("Instance",
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static);
var instance = instanceField?.GetValue(null);
if (instance != null)
{
var optionsType = serializerAssembly.GetType("MessagePack.MessagePackSerializerOptions");
var standardProperty = optionsType?.GetProperty("Standard", BindingFlags.Public | BindingFlags.Static);
var standardOptions = standardProperty?.GetValue(null);
var withResolverMethod = optionsType?.GetMethod("WithResolver", BindingFlags.Public | BindingFlags.Instance);
var resolvedOptions = withResolverMethod?.Invoke(standardOptions, new[] { instance });
if (resolvedOptions != null)
return resolvedOptions;
}
}
return null;
}
private static object? GetDefault(Type type)
{
return type.IsValueType ? Activator.CreateInstance(type) : null;
}
}
}