// Copyright (c) ppy Pty Ltd . 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 { /// /// 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. /// public class SignalRUnionWorkaroundResolver : IFormatterResolver { public static readonly MessagePackSerializerOptions OPTIONS = MessagePackSerializerOptions.Standard.WithResolver(new SignalRUnionWorkaroundResolver()); private static readonly Dictionary formatter_map = new Dictionary { { typeof(TeamVersusUserState), new TypeRedirectingFormatter() }, { typeof(TeamVersusRoomState), new TypeRedirectingFormatter() }, { typeof(ChangeTeamRequest), new TypeRedirectingFormatter() }, // 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() }, { typeof(MatchRoomState), new TypeRedirectingFormatter() }, { typeof(MatchUserRequest), new TypeRedirectingFormatter() }, { typeof(MatchServerEvent), new TypeRedirectingFormatter() }, }; public IMessagePackFormatter GetFormatter() { if (formatter_map.TryGetValue(typeof(T), out var formatter)) return (IMessagePackFormatter)formatter; return StandardResolver.Instance.GetFormatter(); } public class TypeRedirectingFormatter : IMessagePackFormatter { private readonly IMessagePackFormatter baseFormatter; public TypeRedirectingFormatter() { baseFormatter = StandardResolver.Instance.GetFormatter(); } 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); } } }