2021-08-19 08:41:50 +00:00
|
|
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
|
|
|
// See the LICENCE file in the repository root for full licence text.
|
|
|
|
|
|
|
|
using System;
|
|
|
|
using System.Collections.Generic;
|
2021-11-20 12:11:02 +00:00
|
|
|
using System.Linq;
|
2021-08-19 08:41:50 +00:00
|
|
|
using MessagePack;
|
|
|
|
using MessagePack.Formatters;
|
|
|
|
using MessagePack.Resolvers;
|
|
|
|
|
|
|
|
namespace osu.Game.Online
|
|
|
|
{
|
|
|
|
/// <summary>
|
|
|
|
/// Handles SignalR being unable to comprehend [Union] types correctly by redirecting to a known base (union) type.
|
|
|
|
/// See https://github.com/dotnet/aspnetcore/issues/7298.
|
|
|
|
/// </summary>
|
|
|
|
public class SignalRUnionWorkaroundResolver : IFormatterResolver
|
|
|
|
{
|
|
|
|
public static readonly MessagePackSerializerOptions OPTIONS =
|
|
|
|
MessagePackSerializerOptions.Standard.WithResolver(new SignalRUnionWorkaroundResolver());
|
|
|
|
|
2021-11-20 12:11:02 +00:00
|
|
|
private static readonly IReadOnlyDictionary<Type, IMessagePackFormatter> formatter_map = createFormatterMap();
|
2021-11-19 16:03:52 +00:00
|
|
|
|
2021-11-20 12:11:02 +00:00
|
|
|
private static IReadOnlyDictionary<Type, IMessagePackFormatter> createFormatterMap()
|
2021-08-19 08:41:50 +00:00
|
|
|
{
|
2021-11-21 03:30:14 +00:00
|
|
|
IEnumerable<(Type derivedType, Type baseType)> baseMap = SignalRWorkaroundTypes.BASE_TYPE_MAPPING;
|
2021-08-19 08:41:50 +00:00
|
|
|
|
2021-11-20 12:11:02 +00:00
|
|
|
// This should not be required. The fallback should work. But something is weird with the way caching is done.
|
2021-08-19 08:41:50 +00:00
|
|
|
// For future adventurers, I would not advise looking into this further. It's likely not worth the effort.
|
2022-02-24 11:11:58 +00:00
|
|
|
baseMap = baseMap.Concat(baseMap.Select(t => (t.baseType, t.baseType)).Distinct());
|
2021-11-20 12:11:02 +00:00
|
|
|
|
2021-11-21 03:30:14 +00:00
|
|
|
return new Dictionary<Type, IMessagePackFormatter>(baseMap.Select(t =>
|
2021-11-20 12:11:02 +00:00
|
|
|
{
|
|
|
|
var formatter = (IMessagePackFormatter)Activator.CreateInstance(typeof(TypeRedirectingFormatter<,>).MakeGenericType(t.derivedType, t.baseType));
|
|
|
|
return new KeyValuePair<Type, IMessagePackFormatter>(t.derivedType, formatter);
|
|
|
|
}));
|
|
|
|
}
|
2021-08-19 08:41:50 +00:00
|
|
|
|
|
|
|
public IMessagePackFormatter<T> GetFormatter<T>()
|
|
|
|
{
|
2021-11-20 12:11:02 +00:00
|
|
|
if (formatter_map.TryGetValue(typeof(T), out var formatter))
|
2021-08-19 08:41:50 +00:00
|
|
|
return (IMessagePackFormatter<T>)formatter;
|
|
|
|
|
|
|
|
return StandardResolver.Instance.GetFormatter<T>();
|
|
|
|
}
|
|
|
|
|
|
|
|
public class TypeRedirectingFormatter<TActual, TBase> : IMessagePackFormatter<TActual>
|
|
|
|
{
|
|
|
|
private readonly IMessagePackFormatter<TBase> baseFormatter;
|
|
|
|
|
|
|
|
public TypeRedirectingFormatter()
|
|
|
|
{
|
|
|
|
baseFormatter = StandardResolver.Instance.GetFormatter<TBase>();
|
|
|
|
}
|
|
|
|
|
|
|
|
public void Serialize(ref MessagePackWriter writer, TActual value, MessagePackSerializerOptions options) =>
|
|
|
|
baseFormatter.Serialize(ref writer, (TBase)(object)value, StandardResolver.Options);
|
|
|
|
|
|
|
|
public TActual Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options) =>
|
|
|
|
(TActual)(object)baseFormatter.Deserialize(ref reader, StandardResolver.Options);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|