~ruther/NosSmooth

ed78350cd67fdebe10bf0a46e97dfab83d9f3f78 — František Boháček 3 years ago f9ebf7b
feat: add support for specific serializers

Specific serializers give the ability to add custom behavior for some packets that may be harder to serialize
M Core/NosSmooth.Core/Extensions/ServiceCollectionExtensions.cs => Core/NosSmooth.Core/Extensions/ServiceCollectionExtensions.cs +13 -0
@@ 166,4 166,17 @@ public static class ServiceCollectionExtensions

        return serviceCollection;
    }

    /// <summary>
    /// Adds the specified packet converter.
    /// </summary>
    /// <param name="serviceCollection">The service collection to register the responder to.</param>
    /// <typeparam name="TPacketConverter">The type of the packet.</typeparam>
    /// <exception cref="ArgumentException">Thrown if the type of the responder is incorrect.</exception>
    /// <returns>The collection.</returns>
    public static IServiceCollection AddSpecificPacketConverter<TPacketConverter>(this IServiceCollection serviceCollection)
        where TPacketConverter : class, ISpecificPacketSerializer
    {
        return serviceCollection.AddSingleton<ISpecificPacketSerializer, TPacketConverter>();
    }
}
\ No newline at end of file

A Core/NosSmooth.Core/Packets/Converters/ISpecificPacketSerializer.cs => Core/NosSmooth.Core/Packets/Converters/ISpecificPacketSerializer.cs +132 -0
@@ 0,0 1,132 @@
//
//  ISpecificPacketSerializer.cs
//
//  Copyright (c) František Boháček. All rights reserved.
//  Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.Reflection;
using NosCore.Packets.Attributes;
using NosCore.Packets.Interfaces;
using Remora.Results;

namespace NosSmooth.Core.Packets.Converters;

/// <summary>
/// Converts packets that cannot be handled easily by the default serializer.
/// </summary>
public interface ISpecificPacketSerializer
{
    /// <summary>
    /// Gets whether this is a serializer.
    /// </summary>
    public bool Serializer { get; }

    /// <summary>
    /// Gets whether this is a deserializer.
    /// </summary>
    public bool Deserializer { get; }

    /// <summary>
    /// Whether the current packet serializer should handle the packet.
    /// </summary>
    /// <param name="packetString">The string of the packet.</param>
    /// <returns>If this serializer should be used for handling.</returns>
    public bool ShouldHandle(string packetString);

    /// <summary>
    /// Whether the current packet serializer should handle the packet.
    /// </summary>
    /// <param name="packet">The packet object.</param>
    /// <returns>If this serializer should be used for handling.</returns>
    public bool ShouldHandle(IPacket packet);

    /// <summary>
    /// Serialize the given packet into string.
    /// </summary>
    /// <param name="packet">The string of the packet.</param>
    /// <returns>The serialized packet or an error.</returns>
    public Result<string> Serialize(IPacket packet);

    /// <summary>
    /// Deserialize the given packet to its type.
    /// </summary>
    /// <param name="packetString">The string of the packet.</param>
    /// <returns>The deserialized packet or an error.</returns>
    public Result<IPacket> Deserialize(string packetString);
}

/// <summary>
/// Converts packets that cannot be handled easily by the default serializer.
/// </summary>
/// <typeparam name="TPacket">The packet.</typeparam>
public abstract class SpecificPacketSerializer<TPacket> : ISpecificPacketSerializer
    where TPacket : IPacket
{
    private string? _packetHeader;

    /// <summary>
    /// Gets the packet header identifier.
    /// </summary>
    public string PacketHeader
    {
        get
        {
            if (_packetHeader is null)
            {
                _packetHeader = typeof(TPacket).GetCustomAttribute<PacketHeaderAttribute>()!.Identification + " ";
            }

            return _packetHeader;
        }
    }

    /// <inheritdoc />
    public abstract bool Serializer { get; }

    /// <inheritdoc />
    public abstract bool Deserializer { get; }

    /// <inheritdoc />
    public bool ShouldHandle(string packetString)
    {
        return packetString.StartsWith(PacketHeader);
    }

    /// <inheritdoc />
    public bool ShouldHandle(IPacket packet)
    {
        return typeof(TPacket) == packet.GetType();
    }

    /// <inheritdoc/>
    Result<string> ISpecificPacketSerializer.Serialize(IPacket packet)
    {
        return Serialize((TPacket)packet);
    }

    /// <inheritdoc/>
    Result<IPacket> ISpecificPacketSerializer.Deserialize(string packetString)
    {
        var result = Deserialize(packetString);
        if (!result.IsSuccess)
        {
            return Result<IPacket>.FromError(result);
        }

        return Result<IPacket>.FromSuccess(result.Entity);
    }

    /// <summary>
    /// Serialize the given packet into string.
    /// </summary>
    /// <param name="packet">The string of the packet.</param>
    /// <returns>The serialized packet or an error.</returns>
    public abstract Result<string> Serialize(TPacket packet);

    /// <summary>
    /// Deserialize the given packet to its type.
    /// </summary>
    /// <param name="packetString">The string of the packet.</param>
    /// <returns>The deserialized packet or an error.</returns>
    public abstract Result<TPacket> Deserialize(string packetString);
}
\ No newline at end of file

M Core/NosSmooth.Core/Packets/IPacketSerializer.cs => Core/NosSmooth.Core/Packets/IPacketSerializer.cs +14 -0
@@ 4,6 4,8 @@
//  Copyright (c) František Boháček. All rights reserved.
//  Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;
using NosCore.Packets;
using NosCore.Packets.Interfaces;
using Remora.Results;



@@ 27,4 29,16 @@ public interface IPacketSerializer
    /// <param name="packetString">The packet to deserialize.</param>
    /// <returns>The deserialized packet.</returns>
    public Result<IPacket> Deserialize(string packetString);

    /// <summary>
    /// Gets the inner serializer from NosCore.
    /// </summary>
    [Obsolete("May be removed anytime.")]
    public Serializer Serializer { get; }

    /// <summary>
    /// Gets the inner deserializer from NosCore.
    /// </summary>
    [Obsolete("May be removed anytime.")]
    public Deserializer Deserializer { get; }
}
\ No newline at end of file

M Core/NosSmooth.Core/Packets/PacketSerializer.cs => Core/NosSmooth.Core/Packets/PacketSerializer.cs +33 -2
@@ 5,9 5,10 @@
//  Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;
using System.Threading.Tasks;
using System.Collections.Generic;
using NosCore.Packets;
using NosCore.Packets.Interfaces;
using NosSmooth.Core.Packets.Converters;
using Remora.Results;

namespace NosSmooth.Core.Packets;


@@ 17,16 18,24 @@ public class PacketSerializer : IPacketSerializer
{
    private readonly Serializer _serializer;
    private readonly Deserializer _deserializer;
    private readonly IEnumerable<ISpecificPacketSerializer> _specificPacketSerializers;

    /// <summary>
    /// Initializes a new instance of the <see cref="PacketSerializer"/> class.
    /// </summary>
    /// <param name="serializer">The NosCore serializer.</param>
    /// <param name="deserializer">The NosCore deserializer.</param>
    public PacketSerializer(Serializer serializer, Deserializer deserializer)
    /// <param name="specificPacketSerializers">The specific packet serializers.</param>
    public PacketSerializer
    (
        Serializer serializer,
        Deserializer deserializer,
        IEnumerable<ISpecificPacketSerializer> specificPacketSerializers
    )
    {
        _serializer = serializer;
        _deserializer = deserializer;
        _specificPacketSerializers = specificPacketSerializers;
    }

    /// <inheritdoc />


@@ 34,6 43,14 @@ public class PacketSerializer : IPacketSerializer
    {
        try
        {
            foreach (var specificPacketSerializer in _specificPacketSerializers)
            {
                if (specificPacketSerializer.Serializer && specificPacketSerializer.ShouldHandle(packet))
                {
                    return specificPacketSerializer.Serialize(packet);
                }
            }

            return _serializer.Serialize(packet);
        }
        catch (Exception e)


@@ 47,6 64,14 @@ public class PacketSerializer : IPacketSerializer
    {
        try
        {
            foreach (var specificPacketSerializer in _specificPacketSerializers)
            {
                if (specificPacketSerializer.Deserializer && specificPacketSerializer.ShouldHandle(packetString))
                {
                    return specificPacketSerializer.Deserialize(packetString);
                }
            }

            return Result<IPacket>.FromSuccess(_deserializer.Deserialize(packetString));
        }
        catch (Exception e)


@@ 54,4 79,10 @@ public class PacketSerializer : IPacketSerializer
            return e;
        }
    }

    /// <inheritdoc />
    public Serializer Serializer => _serializer;

    /// <inheritdoc />
    public Deserializer Deserializer => _deserializer;
}
\ No newline at end of file

M Core/NosSmooth.Core/Packets/PacketSerializerProvider.cs => Core/NosSmooth.Core/Packets/PacketSerializerProvider.cs +18 -3
@@ 6,7 6,9 @@

using System;
using System.Collections.Generic;
using Microsoft.Extensions.DependencyInjection;
using NosCore.Packets;
using NosSmooth.Core.Packets.Converters;

namespace NosSmooth.Core.Packets;



@@ 20,16 22,19 @@ public class PacketSerializerProvider

    private IPacketSerializer? _serverSerializer;
    private IPacketSerializer? _clientSerializer;
    private IServiceProvider _serviceProvider;

    /// <summary>
    /// Initializes a new instance of the <see cref="PacketSerializerProvider"/> class.
    /// </summary>
    /// <param name="clientPacketTypes">The types of client packets.</param>
    /// <param name="serverPacketTypes">The types of server packets.</param>
    public PacketSerializerProvider(List<Type> clientPacketTypes, List<Type> serverPacketTypes)
    /// <param name="serviceProvider">The service provider for dependency injection.</param>
    public PacketSerializerProvider(List<Type> clientPacketTypes, List<Type> serverPacketTypes, IServiceProvider serviceProvider)
    {
        _clientPacketTypes = clientPacketTypes;
        _serverPacketTypes = serverPacketTypes;
        _serviceProvider = serviceProvider;
    }

    /// <summary>


@@ 42,7 47,12 @@ public class PacketSerializerProvider
            if (_serverSerializer is null)
            {
                _serverSerializer =
                    new PacketSerializer(new Serializer(_serverPacketTypes), new Deserializer(_serverPacketTypes));
                    new PacketSerializer
                    (
                        new Serializer(_serverPacketTypes),
                        new Deserializer(_serverPacketTypes),
                        _serviceProvider.GetServices<ISpecificPacketSerializer>()
                    );
            }

            return _serverSerializer;


@@ 59,7 69,12 @@ public class PacketSerializerProvider
            if (_clientSerializer is null)
            {
                _clientSerializer =
                    new PacketSerializer(new Serializer(_clientPacketTypes), new Deserializer(_clientPacketTypes));
                    new PacketSerializer
                    (
                        new Serializer(_clientPacketTypes),
                        new Deserializer(_clientPacketTypes),
                        _serviceProvider.GetServices<ISpecificPacketSerializer>()
                    );
            }

            return _clientSerializer;

Do not follow this link