From 551cf777ad2d178af63e93ebffedac9d17d5ca3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franti=C5=A1ek=20Boh=C3=A1=C4=8Dek?= Date: Sat, 7 Jan 2023 12:03:17 +0100 Subject: [PATCH] feat(packets): add nullable wrapper to represent a compoud type that may be null as a whole --- .../NullableWrapper.cs | 31 ++++++++ .../Common/NullableWrapperConverter.cs | 79 +++++++++++++++++++ .../Common/NullableWrapperConverterFactory.cs | 57 +++++++++++++ .../SourceGenerator.cs | 6 ++ .../Server/Login/CListPacket.cs | 3 +- .../Server/Mates/ScNEquipmentSubPacket.cs | 8 +- .../Server/Mates/ScNPacket.cs | 17 ++-- .../Server/Mates/ScNSkillSubPacket.cs | 6 +- .../Server/Mates/ScNSpSubPacket.cs | 6 +- 9 files changed, 194 insertions(+), 19 deletions(-) create mode 100644 Packets/NosSmooth.PacketSerializer.Abstractions/NullableWrapper.cs create mode 100644 Packets/NosSmooth.PacketSerializer/Converters/Common/NullableWrapperConverter.cs create mode 100644 Packets/NosSmooth.PacketSerializer/Converters/Common/NullableWrapperConverterFactory.cs diff --git a/Packets/NosSmooth.PacketSerializer.Abstractions/NullableWrapper.cs b/Packets/NosSmooth.PacketSerializer.Abstractions/NullableWrapper.cs new file mode 100644 index 0000000..7601a07 --- /dev/null +++ b/Packets/NosSmooth.PacketSerializer.Abstractions/NullableWrapper.cs @@ -0,0 +1,31 @@ +// +// NullableWrapper.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; + +/// +/// Wraps a compound value that may not be present +/// and there will be "-1" instead in the packet. +/// The converter of underlying type will be called +/// if and only if the value is not null. +/// +/// The value. +/// The underlying type. +[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1313:Parameter names should begin with lower-case letter", Justification = "Fix this, this should not happen.")] +public record struct NullableWrapper(T? Value) +{ + /// + /// Unwrap the underlying value. + /// + /// The wrapper to unwrap. + /// The unwrapped value. + public static implicit operator T?(NullableWrapper wrapper) + { + return wrapper.Value; + } +} \ No newline at end of file diff --git a/Packets/NosSmooth.PacketSerializer/Converters/Common/NullableWrapperConverter.cs b/Packets/NosSmooth.PacketSerializer/Converters/Common/NullableWrapperConverter.cs new file mode 100644 index 0000000..ba83b82 --- /dev/null +++ b/Packets/NosSmooth.PacketSerializer/Converters/Common/NullableWrapperConverter.cs @@ -0,0 +1,79 @@ +// +// NullableWrapperConverter.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; + +/// +/// Converter of . +/// +/// The underlying type. +public class NullableWrapperConverter : BaseStringConverter> +{ + private readonly StringConverterRepository _converterRepository; + + /// + /// Initializes a new instance of the class. + /// + /// The converter repository. + public NullableWrapperConverter(StringConverterRepository converterRepository) + { + _converterRepository = converterRepository; + } + + /// + public override Result Serialize(NullableWrapper obj, PacketStringBuilder builder) + { + if (obj.Value is null) + { + builder.Append("-1"); + } + else + { + var converterResult = _converterRepository.GetTypeConverter(); + if (!converterResult.IsDefined(out var converter)) + { + return Result.FromError(converterResult); + } + + return converter.Serialize(obj.Value, builder); + } + + return Result.FromSuccess(); + } + + /// + public override Result> Deserialize(ref PacketStringEnumerator stringEnumerator) + { + var tokenResult = stringEnumerator.GetNextToken(out var packetToken, false); + if (!tokenResult.IsSuccess) + { + return Result>.FromError(tokenResult); + } + + if (packetToken.Token.Length == 2 && packetToken.Token.StartsWith("-1")) + { + return Result>.FromSuccess(new NullableWrapper(default)); + } + + var converterResult = _converterRepository.GetTypeConverter(); + if (!converterResult.IsDefined(out var converter)) + { + return Result>.FromError(converterResult); + } + + var deserializationResult = converter.Deserialize(ref stringEnumerator); + if (!deserializationResult.IsDefined(out var deserialization)) + { + return Result>.FromError(deserializationResult); + } + + return new NullableWrapper(deserialization); + } +} \ No newline at end of file diff --git a/Packets/NosSmooth.PacketSerializer/Converters/Common/NullableWrapperConverterFactory.cs b/Packets/NosSmooth.PacketSerializer/Converters/Common/NullableWrapperConverterFactory.cs new file mode 100644 index 0000000..fc73c9b --- /dev/null +++ b/Packets/NosSmooth.PacketSerializer/Converters/Common/NullableWrapperConverterFactory.cs @@ -0,0 +1,57 @@ +// +// NullableWrapperConverterFactory.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.Converters.Special.Converters; +using NosSmooth.PacketSerializer.Extensions; +using Remora.Results; + +namespace NosSmooth.PacketSerializer.Converters.Common; + +/// +/// Converts . +/// +public class NullableWrapperConverterFactory : IStringConverterFactory +{ + private readonly IServiceProvider _serviceProvider; + + /// + /// Initializes a new instance of the class. + /// + /// The service provider. + public NullableWrapperConverterFactory(IServiceProvider serviceProvider) + { + _serviceProvider = serviceProvider; + } + + /// + public bool ShouldHandle(Type type) + => type.GetGenericTypeDefinition() == typeof(NullableWrapper<>); + + /// + public Result CreateTypeSerializer(Type type) + { + var underlyingType = type.GetGenericArguments()[0]; + var serializerType = typeof(NullableWrapperConverter<>).MakeGenericType(underlyingType); + + try + { + return Result.FromSuccess + ((IStringConverter)ActivatorUtilities.CreateInstance(_serviceProvider, serializerType)); + } + catch (Exception e) + { + return e; + } + } + + /// + public Result> CreateTypeSerializer() + => CreateTypeSerializer(typeof(T)).Cast, IStringConverter>(); +} \ No newline at end of file diff --git a/Packets/NosSmooth.PacketSerializersGenerator/SourceGenerator.cs b/Packets/NosSmooth.PacketSerializersGenerator/SourceGenerator.cs index 3026003..84223f7 100644 --- a/Packets/NosSmooth.PacketSerializersGenerator/SourceGenerator.cs +++ b/Packets/NosSmooth.PacketSerializersGenerator/SourceGenerator.cs @@ -130,6 +130,12 @@ public class SourceGenerator : ISourceGenerator $"{packetRecord.GetPrefix()}.{packetRecord.Identifier.NormalizeWhitespace().ToFullString()}Converter.g.cs", stringWriter.GetStringBuilder().ToString() ); + + File.WriteAllText + ( + Path.Combine(Path.GetTempPath(), $"{packetRecord.GetPrefix()}.{packetRecord.Identifier.NormalizeWhitespace().ToFullString()}Converter.g.cs"), + stringWriter.GetStringBuilder().ToString() + ); } } diff --git a/Packets/NosSmooth.Packets/Server/Login/CListPacket.cs b/Packets/NosSmooth.Packets/Server/Login/CListPacket.cs index f393d21..966141d 100644 --- a/Packets/NosSmooth.Packets/Server/Login/CListPacket.cs +++ b/Packets/NosSmooth.Packets/Server/Login/CListPacket.cs @@ -6,6 +6,7 @@ using NosSmooth.Packets.Enums.Players; using NosSmooth.Packets.Server.Maps; +using NosSmooth.PacketSerializer.Abstractions; using NosSmooth.PacketSerializer.Abstractions.Attributes; using NosSmooth.PacketSerializer.Abstractions.Common; @@ -73,7 +74,7 @@ public record CListPacket [PacketIndex(14)] byte Unknown3, [PacketListIndex(15, ListSeparator = '.', InnerSeparator = '.')] - IReadOnlyList PetsSubPacket, + IReadOnlyList> PetsSubPacket, [PacketIndex(16)] byte HatDesign, [PacketIndex(17)] diff --git a/Packets/NosSmooth.Packets/Server/Mates/ScNEquipmentSubPacket.cs b/Packets/NosSmooth.Packets/Server/Mates/ScNEquipmentSubPacket.cs index ab6cea0..26c2c6c 100644 --- a/Packets/NosSmooth.Packets/Server/Mates/ScNEquipmentSubPacket.cs +++ b/Packets/NosSmooth.Packets/Server/Mates/ScNEquipmentSubPacket.cs @@ -21,9 +21,9 @@ namespace NosSmooth.Packets.Server.Mates; public record ScNEquipmentSubPacket ( [PacketIndex(0)] - int? ItemVNum, - [PacketIndex(1, IsOptional = true)] + int ItemVNum, + [PacketIndex(1)] sbyte? ItemRare, - [PacketIndex(2, IsOptional = true)] - byte? ItemUpgrade + [PacketIndex(2)] + byte ItemUpgrade ) : IPacket; \ No newline at end of file diff --git a/Packets/NosSmooth.Packets/Server/Mates/ScNPacket.cs b/Packets/NosSmooth.Packets/Server/Mates/ScNPacket.cs index ccdf768..4553ba3 100644 --- a/Packets/NosSmooth.Packets/Server/Mates/ScNPacket.cs +++ b/Packets/NosSmooth.Packets/Server/Mates/ScNPacket.cs @@ -7,6 +7,7 @@ using NosSmooth.Packets.Enums; using NosSmooth.Packets.Server.Character; using NosSmooth.Packets.Server.Maps; +using NosSmooth.PacketSerializer.Abstractions; using NosSmooth.PacketSerializer.Abstractions.Attributes; using NosSmooth.PacketSerializer.Abstractions.Common; @@ -73,13 +74,13 @@ public record ScNPacket [PacketIndex(5)] long Experience, [PacketIndex(6, InnerSeparator = '.')] - ScNEquipmentSubPacket? WeaponSubPacket, + NullableWrapper WeaponSubPacket, [PacketIndex(7, InnerSeparator = '.')] - ScNEquipmentSubPacket? ArmorSubPacket, + NullableWrapper ArmorSubPacket, [PacketIndex(8, InnerSeparator = '.')] - ScNEquipmentSubPacket? GauntletSubPacket, + NullableWrapper GauntletSubPacket, [PacketIndex(9, InnerSeparator = '.')] - ScNEquipmentSubPacket? BootsSubPacket, + NullableWrapper BootsSubPacket, [PacketIndex(10, InnerSeparator = '.')] short Unknown1, [PacketIndex(11)] @@ -133,11 +134,11 @@ public record ScNPacket [PacketIndex(35)] bool IsSummonable, [PacketIndex(36, InnerSeparator = '.')] - ScNSpSubPacket? SpSubPacket, + NullableWrapper SpSubPacket, [PacketIndex(37, InnerSeparator = '.')] - ScNSkillSubPacket? Skill1SubPacket, + NullableWrapper Skill1SubPacket, [PacketIndex(38, InnerSeparator = '.')] - ScNSkillSubPacket? Skill2SubPacket, + NullableWrapper Skill2SubPacket, [PacketIndex(39, InnerSeparator = '.')] - ScNSkillSubPacket? Skill3SubPacket + NullableWrapper Skill3SubPacket ) : IPacket; \ No newline at end of file diff --git a/Packets/NosSmooth.Packets/Server/Mates/ScNSkillSubPacket.cs b/Packets/NosSmooth.Packets/Server/Mates/ScNSkillSubPacket.cs index fbf3132..7a5d5a7 100644 --- a/Packets/NosSmooth.Packets/Server/Mates/ScNSkillSubPacket.cs +++ b/Packets/NosSmooth.Packets/Server/Mates/ScNSkillSubPacket.cs @@ -21,7 +21,7 @@ namespace NosSmooth.Packets.Server.Mates; public record ScNSkillSubPacket ( [PacketIndex(0)] - int? SkillVNum, - [PacketIndex(1, IsOptional = true)] - PartnerSkillRank? Rank + int SkillVNum, + [PacketIndex(1)] + PartnerSkillRank Rank ) : IPacket; \ No newline at end of file diff --git a/Packets/NosSmooth.Packets/Server/Mates/ScNSpSubPacket.cs b/Packets/NosSmooth.Packets/Server/Mates/ScNSpSubPacket.cs index a1ba231..35940dc 100644 --- a/Packets/NosSmooth.Packets/Server/Mates/ScNSpSubPacket.cs +++ b/Packets/NosSmooth.Packets/Server/Mates/ScNSpSubPacket.cs @@ -20,7 +20,7 @@ namespace NosSmooth.Packets.Server.Mates; public record ScNSpSubPacket ( [PacketIndex(0)] - long? ItemVNum, - [PacketIndex(1, IsOptional = true)] - byte? AgilityPercentage + long ItemVNum, + [PacketIndex(1)] + byte AgilityPercentage ); \ No newline at end of file -- 2.49.0