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)]