~ruther/NosSmooth

99dd6e8a59a58bc8ebb173b8422e93b3e094a258 — Rutherther 2 years ago 3d131cb + 6e97f92
Merge pull request #52 from Rutherther/feat/optional-wrapper

Add optional wrapper
M Core/NosSmooth.Game/Errors/NotInRangeError.cs => Core/NosSmooth.Game/Errors/NotInRangeError.cs +7 -0
@@ 9,6 9,13 @@ using Remora.Results;

namespace NosSmooth.Game.Errors;

/// <summary>
/// The given position is not in range of the entity.
/// </summary>
/// <param name="Entity">The entity.</param>
/// <param name="EntityPosition">The entity's position.</param>
/// <param name="TargetPosition">The target position.</param>
/// <param name="Range">The maximum range.</param>
public record NotInRangeError
(
    string Entity,

M Packets/NosSmooth.PacketSerializer.Abstractions/NullableWrapper.cs => Packets/NosSmooth.PacketSerializer.Abstractions/NullableWrapper.cs +5 -0
@@ 29,6 29,11 @@ public record struct NullableWrapper<T>(T? Value)
        return wrapper.Value;
    }

    /// <summary>
    /// Wrap the value in nullable wrapper.
    /// </summary>
    /// <param name="value">The value to wrap.</param>
    /// <returns>The wrapped value.</returns>
    public static implicit operator NullableWrapper<T>(T? value)
    {
        return new NullableWrapper<T>(value);

A Packets/NosSmooth.PacketSerializer.Abstractions/OptionalWrapper.cs => Packets/NosSmooth.PacketSerializer.Abstractions/OptionalWrapper.cs +41 -0
@@ 0,0 1,41 @@
//
//  OptionalWrapper.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.Diagnostics.CodeAnalysis;

namespace NosSmooth.PacketSerializer.Abstractions;

/// <summary>
/// Wraps a compound value that may not be present
/// and there will be nothing instead in the packet.
/// The converter of underlying type will be called
/// if and only if the value is not null.
/// </summary>
/// <param name="Value">The value.</param>
/// <typeparam name="T">The underlying type.</typeparam>
[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1313:Parameter names should begin with lower-case letter", Justification = "Fix this, this should not happen.")]
public record struct OptionalWrapper<T>(bool Present, T? Value)
{
    /// <summary>
    /// Unwrap the underlying value.
    /// </summary>
    /// <param name="wrapper">The wrapper to unwrap.</param>
    /// <returns>The unwrapped value.</returns>
    public static implicit operator T?(OptionalWrapper<T> wrapper)
    {
        return wrapper.Value;
    }

    /// <summary>
    /// wrap the value in optional wrapper.
    /// </summary>
    /// <param name="value">The value to wrap.</param>
    /// <returns>The wrapped value.</returns>
    public static implicit operator OptionalWrapper<T>(T? value)
    {
        return new OptionalWrapper<T>(true, value);
    }
}
\ No newline at end of file

A Packets/NosSmooth.PacketSerializer/Converters/Common/OptionalWrapperConverter.cs => Packets/NosSmooth.PacketSerializer/Converters/Common/OptionalWrapperConverter.cs +82 -0
@@ 0,0 1,82 @@
//
//  OptionalWrapperConverter.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;
using NosSmooth.PacketSerializer.Abstractions;
using Remora.Results;

namespace NosSmooth.PacketSerializer.Converters.Common;

/// <summary>
/// Converter of <see cref="OptionalWrapper{T}"/>.
/// </summary>
/// <typeparam name="T">The underlying type.</typeparam>
public class OptionalWrapperConverter<T> : BaseStringConverter<OptionalWrapper<T>>
{
    private readonly IStringConverterRepository _converterRepository;

    /// <summary>
    /// Initializes a new instance of the <see cref="OptionalWrapperConverter{T}"/> class.
    /// </summary>
    /// <param name="converterRepository">The converter repository.</param>
    public OptionalWrapperConverter(IStringConverterRepository converterRepository)
    {
        _converterRepository = converterRepository;
    }

    /// <inheritdoc />
    public override Result Serialize(OptionalWrapper<T> obj, PacketStringBuilder builder)
    {
        if (obj.Value is null)
        {
            return Result.FromSuccess();
        }

        var converterResult = _converterRepository.GetTypeConverter<T>();
        if (!converterResult.IsDefined(out var converter))
        {
            return Result.FromError(converterResult);
        }

        return converter.Serialize(obj.Value, builder);

    }

    /// <inheritdoc />
    public override Result<OptionalWrapper<T>> Deserialize(ref PacketStringEnumerator stringEnumerator, DeserializeOptions options)
    {
        if (stringEnumerator.IsOnLastToken() ?? false)
        {
            return Result<OptionalWrapper<T>>.FromSuccess(new OptionalWrapper<T>(false, default));
        }

        var tokenResult = stringEnumerator.GetNextToken(out var token, false);
        if (!tokenResult.IsSuccess)
        {
            return Result<OptionalWrapper<T>>.FromError(tokenResult);
        }

        if (token.Token.Length == 0)
        {
            stringEnumerator.GetNextToken(out _); // seek
            return Result<OptionalWrapper<T>>.FromSuccess(new OptionalWrapper<T>(false, default));
        }

        var converterResult = _converterRepository.GetTypeConverter<T>();
        if (!converterResult.IsDefined(out var converter))
        {
            return Result<OptionalWrapper<T>>.FromError(converterResult);
        }

        var deserializationResult = converter.Deserialize(ref stringEnumerator, new DeserializeOptions(true));
        if (!deserializationResult.IsDefined(out var deserialization))
        {
            return Result<OptionalWrapper<T>>.FromError(deserializationResult);
        }

        return new OptionalWrapper<T>(true, deserialization);
    }
}
\ No newline at end of file

A Packets/NosSmooth.PacketSerializer/Converters/Common/OptionalWrapperConverterFactory.cs => Packets/NosSmooth.PacketSerializer/Converters/Common/OptionalWrapperConverterFactory.cs +56 -0
@@ 0,0 1,56 @@
//
//  OptionalWrapperConverterFactory.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;
using Microsoft.Extensions.DependencyInjection;
using NosSmooth.PacketSerializer.Abstractions;
using NosSmooth.PacketSerializer.Converters.Special;
using NosSmooth.PacketSerializer.Extensions;
using Remora.Results;

namespace NosSmooth.PacketSerializer.Converters.Common;

/// <summary>
/// Converts <see cref="OptionalWrapper{T}"/>.
/// </summary>
public class OptionalWrapperConverterFactory : IStringConverterFactory
{
    private readonly IServiceProvider _serviceProvider;

    /// <summary>
    /// Initializes a new instance of the <see cref="OptionalWrapperConverterFactory"/> class.
    /// </summary>
    /// <param name="serviceProvider">The service provider.</param>
    public OptionalWrapperConverterFactory(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
    }

    /// <inheritdoc />
    public bool ShouldHandle(Type type)
        => type.GetGenericTypeDefinition() == typeof(OptionalWrapper<>);

    /// <inheritdoc />
    public Result<IStringConverter> CreateTypeSerializer(Type type)
    {
        var underlyingType = type.GetGenericArguments()[0];
        var serializerType = typeof(OptionalWrapperConverter<>).MakeGenericType(underlyingType);

        try
        {
            return Result<IStringConverter>.FromSuccess
                ((IStringConverter)ActivatorUtilities.CreateInstance(_serviceProvider, serializerType));
        }
        catch (Exception e)
        {
            return e;
        }
    }

    /// <inheritdoc />
    public Result<IStringConverter<T>> CreateTypeSerializer<T>()
        => CreateTypeSerializer(typeof(T)).Cast<IStringConverter<T>, IStringConverter>();
}
\ No newline at end of file

M Packets/NosSmooth.PacketSerializer/Extensions/ServiceCollectionExtensions.cs => Packets/NosSmooth.PacketSerializer/Extensions/ServiceCollectionExtensions.cs +1 -0
@@ 78,6 78,7 @@ public static class ServiceCollectionExtensions
            .AddStringConverterFactory<NullableStringConverterFactory>()
            .AddStringConverterFactory<EnumStringConverterFactory>()
            .AddStringConverterFactory<NullableWrapperConverterFactory>()
            .AddStringConverterFactory<OptionalWrapperConverterFactory>()
            .AddStringConverter<IntStringConverter>()
            .AddStringConverter<BoolStringConverter>()
            .AddStringConverter<UIntStringConverter>()

M Packets/NosSmooth.Packets/Client/Battle/UsePartnerSkillPacket.cs => Packets/NosSmooth.Packets/Client/Battle/UsePartnerSkillPacket.cs +1 -0
@@ 15,6 15,7 @@ namespace NosSmooth.Packets.Client.Battle;
/// <param name="MateTransportId">The pet skill id.</param>
/// <param name="TargetEntityType">The target entity type.</param>
/// <param name="TargetId">The target id.</param>
/// <param name="SkillSlot">The slot of the skill.</param>
/// <param name="MapX">The x coordinate to target to, present if the skill is area skill.</param>
/// <param name="MapY">The y coordinate to target to, present if the skill is area skill.</param>
[PacketHeader("u_ps", PacketSource.Client)]

M Packets/NosSmooth.Packets/Client/Movement/PreqPacket.cs => Packets/NosSmooth.Packets/Client/Movement/PreqPacket.cs +1 -1
@@ 13,4 13,4 @@ namespace NosSmooth.Packets.Client.Movement;
/// </summary>
[PacketHeader("preq", PacketSource.Server)]
[GenerateSerializer(true)]
public record PreqPacket : IPacket;
\ No newline at end of file
public record PreqPacket() : IPacket;
\ No newline at end of file

M Packets/NosSmooth.Packets/Server/Login/CListPacket.cs => Packets/NosSmooth.Packets/Server/Login/CListPacket.cs +1 -1
@@ 74,7 74,7 @@ public record CListPacket
    [PacketIndex(14)]
    byte Unknown3,
    [PacketListIndex(15, ListSeparator = '.', InnerSeparator = '.')]
    IReadOnlyList<NullableWrapper<CListPetSubPacket>> PetsSubPacket,
    IReadOnlyList<OptionalWrapper<NullableWrapper<CListPetSubPacket>>> PetsSubPacket,
    [PacketIndex(16)]
    byte HatDesign,
    [PacketIndex(17)]

Do not follow this link