A Packets/NosSmooth.PacketSerializer.Abstractions/NullableWrapper.cs => Packets/NosSmooth.PacketSerializer.Abstractions/NullableWrapper.cs +31 -0
@@ 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;
+
+/// <summary>
+/// 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.
+/// </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 NullableWrapper<T>(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?(NullableWrapper<T> wrapper)
+ {
+ return wrapper.Value;
+ }
+}<
\ No newline at end of file
A Packets/NosSmooth.PacketSerializer/Converters/Common/NullableWrapperConverter.cs => Packets/NosSmooth.PacketSerializer/Converters/Common/NullableWrapperConverter.cs +79 -0
@@ 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;
+
+/// <summary>
+/// Converter of <see cref="NullableWrapper{T}"/>.
+/// </summary>
+/// <typeparam name="T">The underlying type.</typeparam>
+public class NullableWrapperConverter<T> : BaseStringConverter<NullableWrapper<T>>
+{
+ private readonly StringConverterRepository _converterRepository;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="NullableWrapperConverter{T}"/> class.
+ /// </summary>
+ /// <param name="converterRepository">The converter repository.</param>
+ public NullableWrapperConverter(StringConverterRepository converterRepository)
+ {
+ _converterRepository = converterRepository;
+ }
+
+ /// <inheritdoc />
+ public override Result Serialize(NullableWrapper<T> obj, PacketStringBuilder builder)
+ {
+ if (obj.Value is null)
+ {
+ builder.Append("-1");
+ }
+ else
+ {
+ var converterResult = _converterRepository.GetTypeConverter<T>();
+ if (!converterResult.IsDefined(out var converter))
+ {
+ return Result.FromError(converterResult);
+ }
+
+ return converter.Serialize(obj.Value, builder);
+ }
+
+ return Result.FromSuccess();
+ }
+
+ /// <inheritdoc />
+ public override Result<NullableWrapper<T>> Deserialize(ref PacketStringEnumerator stringEnumerator)
+ {
+ var tokenResult = stringEnumerator.GetNextToken(out var packetToken, false);
+ if (!tokenResult.IsSuccess)
+ {
+ return Result<NullableWrapper<T>>.FromError(tokenResult);
+ }
+
+ if (packetToken.Token.Length == 2 && packetToken.Token.StartsWith("-1"))
+ {
+ return Result<NullableWrapper<T>>.FromSuccess(new NullableWrapper<T>(default));
+ }
+
+ var converterResult = _converterRepository.GetTypeConverter<T>();
+ if (!converterResult.IsDefined(out var converter))
+ {
+ return Result<NullableWrapper<T>>.FromError(converterResult);
+ }
+
+ var deserializationResult = converter.Deserialize(ref stringEnumerator);
+ if (!deserializationResult.IsDefined(out var deserialization))
+ {
+ return Result<NullableWrapper<T>>.FromError(deserializationResult);
+ }
+
+ return new NullableWrapper<T>(deserialization);
+ }
+}<
\ No newline at end of file
A Packets/NosSmooth.PacketSerializer/Converters/Common/NullableWrapperConverterFactory.cs => Packets/NosSmooth.PacketSerializer/Converters/Common/NullableWrapperConverterFactory.cs +57 -0
@@ 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;
+
+/// <summary>
+/// Converts <see cref="NullableWrapper{T}"/>.
+/// </summary>
+public class NullableWrapperConverterFactory : IStringConverterFactory
+{
+ private readonly IServiceProvider _serviceProvider;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="NullableWrapperConverterFactory"/> class.
+ /// </summary>
+ /// <param name="serviceProvider">The service provider.</param>
+ public NullableWrapperConverterFactory(IServiceProvider serviceProvider)
+ {
+ _serviceProvider = serviceProvider;
+ }
+
+ /// <inheritdoc />
+ public bool ShouldHandle(Type type)
+ => type.GetGenericTypeDefinition() == typeof(NullableWrapper<>);
+
+ /// <inheritdoc />
+ public Result<IStringConverter> CreateTypeSerializer(Type type)
+ {
+ var underlyingType = type.GetGenericArguments()[0];
+ var serializerType = typeof(NullableWrapperConverter<>).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.PacketSerializersGenerator/SourceGenerator.cs => Packets/NosSmooth.PacketSerializersGenerator/SourceGenerator.cs +6 -0
@@ 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()
+ );
}
}
M Packets/NosSmooth.Packets/Server/Login/CListPacket.cs => Packets/NosSmooth.Packets/Server/Login/CListPacket.cs +2 -1
@@ 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<CListPetSubPacket> PetsSubPacket,
+ IReadOnlyList<NullableWrapper<CListPetSubPacket>> PetsSubPacket,
[PacketIndex(16)]
byte HatDesign,
[PacketIndex(17)]
M Packets/NosSmooth.Packets/Server/Mates/ScNEquipmentSubPacket.cs => Packets/NosSmooth.Packets/Server/Mates/ScNEquipmentSubPacket.cs +4 -4
@@ 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
M Packets/NosSmooth.Packets/Server/Mates/ScNPacket.cs => Packets/NosSmooth.Packets/Server/Mates/ScNPacket.cs +9 -8
@@ 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<ScNEquipmentSubPacket> WeaponSubPacket,
[PacketIndex(7, InnerSeparator = '.')]
- ScNEquipmentSubPacket? ArmorSubPacket,
+ NullableWrapper<ScNEquipmentSubPacket> ArmorSubPacket,
[PacketIndex(8, InnerSeparator = '.')]
- ScNEquipmentSubPacket? GauntletSubPacket,
+ NullableWrapper<ScNEquipmentSubPacket> GauntletSubPacket,
[PacketIndex(9, InnerSeparator = '.')]
- ScNEquipmentSubPacket? BootsSubPacket,
+ NullableWrapper<ScNEquipmentSubPacket> 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<ScNSpSubPacket> SpSubPacket,
[PacketIndex(37, InnerSeparator = '.')]
- ScNSkillSubPacket? Skill1SubPacket,
+ NullableWrapper<ScNSkillSubPacket> Skill1SubPacket,
[PacketIndex(38, InnerSeparator = '.')]
- ScNSkillSubPacket? Skill2SubPacket,
+ NullableWrapper<ScNSkillSubPacket> Skill2SubPacket,
[PacketIndex(39, InnerSeparator = '.')]
- ScNSkillSubPacket? Skill3SubPacket
+ NullableWrapper<ScNSkillSubPacket> Skill3SubPacket
) : IPacket;=
\ No newline at end of file
M Packets/NosSmooth.Packets/Server/Mates/ScNSkillSubPacket.cs => Packets/NosSmooth.Packets/Server/Mates/ScNSkillSubPacket.cs +3 -3
@@ 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
M Packets/NosSmooth.Packets/Server/Mates/ScNSpSubPacket.cs => Packets/NosSmooth.Packets/Server/Mates/ScNSpSubPacket.cs +3 -3
@@ 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