Add workaround for SignalR union serialisation

This commit is contained in:
Dean Herbert 2021-08-19 17:41:50 +09:00
parent da8a1692d9
commit fa01e4fad2
2 changed files with 67 additions and 1 deletions

View File

@ -148,7 +148,12 @@ namespace osu.Game.Online
});
if (RuntimeInfo.SupportsJIT && preferMessagePack)
builder.AddMessagePackProtocol();
{
builder.AddMessagePackProtocol(options =>
{
options.SerializerOptions = SignalRUnionWorkaroundResolver.OPTIONS;
});
}
else
{
// eventually we will precompile resolvers for messagepack, but this isn't working currently

View File

@ -0,0 +1,61 @@
// 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;
using MessagePack;
using MessagePack.Formatters;
using MessagePack.Resolvers;
using osu.Game.Online.Multiplayer;
using osu.Game.Online.Multiplayer.MatchTypes.TeamVersus;
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());
private static readonly Dictionary<Type, IMessagePackFormatter> formatter_map = new Dictionary<Type, IMessagePackFormatter>
{
{ typeof(TeamVersusUserState), new TypeRedirectingFormatter<TeamVersusUserState, MatchUserState>() },
{ typeof(TeamVersusRoomState), new TypeRedirectingFormatter<TeamVersusRoomState, MatchRoomState>() },
{ typeof(ChangeTeamRequest), new TypeRedirectingFormatter<ChangeTeamRequest, MatchUserRequest>() },
// These should not be required. The fallback should work. But something is weird with the way caching is done.
// For future adventurers, I would not advise looking into this further. It's likely not worth the effort.
{ typeof(MatchUserState), new TypeRedirectingFormatter<MatchUserState, MatchUserState>() },
{ typeof(MatchRoomState), new TypeRedirectingFormatter<MatchRoomState, MatchRoomState>() },
{ typeof(MatchUserRequest), new TypeRedirectingFormatter<MatchUserRequest, MatchUserRequest>() },
{ typeof(MatchServerEvent), new TypeRedirectingFormatter<MatchServerEvent, MatchServerEvent>() },
};
public IMessagePackFormatter<T> GetFormatter<T>()
{
if (formatter_map.TryGetValue(typeof(T), out var formatter))
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);
}
}
}