From 3e52548e23c46738ce2194dd02c79fdce85959c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franti=C5=A1ek=20Boh=C3=A1=C4=8Dek?= Date: Sun, 5 Feb 2023 19:07:52 +0100 Subject: [PATCH] feat(core): split raw client and managed client as well as packet handlers Resolves #65 --- .../Client/BaseNostaleClient.cs | 26 +-- Core/NosSmooth.Core/Client/INostaleClient.cs | 16 -- .../Client/ManagedNostaleClient.cs | 84 ++++++++ .../Extensions/ServiceCollectionExtensions.cs | 26 ++- Core/NosSmooth.Core/Packets/IPacketHandler.cs | 36 ++++ .../Packets/IPostExecutionEvent.cs | 16 ++ .../Packets/IPreExecutionEvent.cs | 17 ++ .../Packets/IRawPacketResponder.cs | 26 +++ ...cketHandler.cs => ManagedPacketHandler.cs} | 115 +++++++++-- .../NosSmooth.Core/Packets/PacketEventArgs.cs | 7 + .../Packets/RawPacketHandler.cs | 188 ++++++++++++++++++ .../Stateful/StatefulPreExecutionEvent.cs | 11 +- .../Apis/Safe/NostaleChatApi.cs | 4 +- .../Apis/Safe/NostaleSkillsApi.cs | 4 +- .../Apis/Unsafe/UnsafeInventoryApi.cs | 4 +- .../Apis/Unsafe/UnsafeMapApi.cs | 4 +- .../Apis/Unsafe/UnsafeMateApi.cs | 4 +- .../Apis/Unsafe/UnsafeMateSkillsApi.cs | 4 +- .../Apis/Unsafe/UnsafeSkillsApi.cs | 4 +- .../Extensions/ServiceCollectionExtensions.cs | 2 +- .../CombatManager.cs | 4 +- .../CombatState.cs | 4 +- .../ICombatState.cs | 2 +- Samples/FileClient/Client.cs | 30 +-- Samples/FileClient/Program.cs | 5 +- .../Fakes/FakeEmptyNostaleClient.cs | 12 -- .../NosSmooth.Core.Tests/Fakes/FakeLogger.cs | 39 ++++ .../Fakes/FakeNostaleClient.cs | 12 -- .../Fakes/Packets/Events/PacketEvent.cs | 15 ++ .../Fakes/Packets/FakePacket.cs | 9 +- .../NosSmooth.Core.Tests.csproj | 8 +- .../Packets/PacketHandlerTests.Events.cs | 14 +- .../Stateful/StatefulInjectorTests.cs | 16 +- 33 files changed, 625 insertions(+), 143 deletions(-) create mode 100644 Core/NosSmooth.Core/Client/ManagedNostaleClient.cs create mode 100644 Core/NosSmooth.Core/Packets/IPacketHandler.cs create mode 100644 Core/NosSmooth.Core/Packets/IRawPacketResponder.cs rename Core/NosSmooth.Core/Packets/{PacketHandler.cs => ManagedPacketHandler.cs} (61%) create mode 100644 Core/NosSmooth.Core/Packets/RawPacketHandler.cs create mode 100644 Tests/NosSmooth.Core.Tests/Fakes/FakeLogger.cs diff --git a/Core/NosSmooth.Core/Client/BaseNostaleClient.cs b/Core/NosSmooth.Core/Client/BaseNostaleClient.cs index cf97ddd..c43fb63 100644 --- a/Core/NosSmooth.Core/Client/BaseNostaleClient.cs +++ b/Core/NosSmooth.Core/Client/BaseNostaleClient.cs @@ -22,52 +22,28 @@ namespace NosSmooth.Core.Client; public abstract class BaseNostaleClient : INostaleClient { private readonly CommandProcessor _commandProcessor; - private readonly IPacketSerializer _packetSerializer; /// /// Initializes a new instance of the class. /// /// The command processor. - /// The packet serializer. protected BaseNostaleClient ( - CommandProcessor commandProcessor, - IPacketSerializer packetSerializer + CommandProcessor commandProcessor ) { _commandProcessor = commandProcessor; - _packetSerializer = packetSerializer; } /// public abstract Task RunAsync(CancellationToken stopRequested = default); - /// - public virtual Task SendPacketAsync(IPacket packet, CancellationToken ct = default) - { - var serialized = _packetSerializer.Serialize(packet); - - return serialized.IsSuccess - ? SendPacketAsync(serialized.Entity, ct) - : Task.FromResult(Result.FromError(serialized)); - } - /// public abstract Task SendPacketAsync(string packetString, CancellationToken ct = default); /// public abstract Task ReceivePacketAsync(string packetString, CancellationToken ct = default); - /// - public virtual Task ReceivePacketAsync(IPacket packet, CancellationToken ct = default) - { - var serialized = _packetSerializer.Serialize(packet); - - return serialized.IsSuccess - ? ReceivePacketAsync(serialized.Entity, ct) - : Task.FromResult(Result.FromError(serialized)); - } - /// public Task SendCommandAsync(ICommand command, CancellationToken ct = default) => _commandProcessor.ProcessCommand(this, command, ct); diff --git a/Core/NosSmooth.Core/Client/INostaleClient.cs b/Core/NosSmooth.Core/Client/INostaleClient.cs index ee3a68d..c183bb7 100644 --- a/Core/NosSmooth.Core/Client/INostaleClient.cs +++ b/Core/NosSmooth.Core/Client/INostaleClient.cs @@ -24,14 +24,6 @@ public interface INostaleClient /// The result that may or may not have succeeded. public Task RunAsync(CancellationToken stopRequested = default); - /// - /// Sends the given packet to the server. - /// - /// The packet to send. - /// The cancellation token for cancelling the operation. - /// A result that may or may not have succeeded. - public Task SendPacketAsync(IPacket packet, CancellationToken ct = default); - /// /// Sends the given raw packet string. /// @@ -48,14 +40,6 @@ public interface INostaleClient /// A result that may or may not have succeeded. public Task ReceivePacketAsync(string packetString, CancellationToken ct = default); - /// - /// Receives the given packet. - /// - /// The packet to receive. - /// The cancellation token for cancelling the operation. - /// A result that may or may not have succeeded. - public Task ReceivePacketAsync(IPacket packet, CancellationToken ct = default); - /// /// Sends the given command to the client. /// diff --git a/Core/NosSmooth.Core/Client/ManagedNostaleClient.cs b/Core/NosSmooth.Core/Client/ManagedNostaleClient.cs new file mode 100644 index 0000000..3f1c4dd --- /dev/null +++ b/Core/NosSmooth.Core/Client/ManagedNostaleClient.cs @@ -0,0 +1,84 @@ +// +// ManagedNostaleClient.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.Threading; +using System.Threading.Tasks; +using NosSmooth.Core.Commands; +using NosSmooth.Packets; +using NosSmooth.PacketSerializer; +using Remora.Results; + +namespace NosSmooth.Core.Client; + +/// +/// A NosTale client that supports sending and receiving packets using . +/// +public class ManagedNostaleClient : INostaleClient +{ + private readonly INostaleClient _rawClient; + private readonly IPacketSerializer _packetSerializer; + + /// + /// Initializes a new instance of the class. + /// + /// The raw nostale client. + /// The packet serializer. + protected ManagedNostaleClient + ( + INostaleClient rawClient, + IPacketSerializer packetSerializer + ) + { + _rawClient = rawClient; + _packetSerializer = packetSerializer; + } + + /// + /// Receives the given packet. + /// + /// The packet to receive. + /// The cancellation token for cancelling the operation. + /// A result that may or may not have succeeded. + public Task ReceivePacketAsync(IPacket packet, CancellationToken ct = default) + { + var serialized = _packetSerializer.Serialize(packet); + + return serialized.IsSuccess + ? ReceivePacketAsync(serialized.Entity, ct) + : Task.FromResult(Result.FromError(serialized)); + } + + /// + /// Sends the given packet to the server. + /// + /// The packet to send. + /// The cancellation token for cancelling the operation. + /// A result that may or may not have succeeded. + public Task SendPacketAsync(IPacket packet, CancellationToken ct = default) + { + var serialized = _packetSerializer.Serialize(packet); + + return serialized.IsSuccess + ? SendPacketAsync(serialized.Entity, ct) + : Task.FromResult(Result.FromError(serialized)); + } + + /// + public Task RunAsync(CancellationToken stopRequested = default) + => _rawClient.RunAsync(stopRequested); + + /// + public Task SendPacketAsync(string packetString, CancellationToken ct = default) + => _rawClient.SendPacketAsync(packetString, ct); + + /// + public Task ReceivePacketAsync(string packetString, CancellationToken ct = default) + => _rawClient.ReceivePacketAsync(packetString, ct); + + /// + public Task SendCommandAsync(ICommand command, CancellationToken ct = default) + => _rawClient.SendCommandAsync(command, ct); +} \ No newline at end of file diff --git a/Core/NosSmooth.Core/Extensions/ServiceCollectionExtensions.cs b/Core/NosSmooth.Core/Extensions/ServiceCollectionExtensions.cs index 7014d16..cb0aa2f 100644 --- a/Core/NosSmooth.Core/Extensions/ServiceCollectionExtensions.cs +++ b/Core/NosSmooth.Core/Extensions/ServiceCollectionExtensions.cs @@ -26,7 +26,7 @@ namespace NosSmooth.Core.Extensions; public static class ServiceCollectionExtensions { /// - /// Adds base packet and command handling for nostale client. + /// Adds base packet (raw packets) and command handling for nostale client. /// /// The service collection to register the responder to. /// The collection. @@ -36,10 +36,30 @@ public static class ServiceCollectionExtensions ) { serviceCollection - .TryAddSingleton(); + .TryAddSingleton(); + serviceCollection.AddSingleton(); + + return serviceCollection; + } + /// + /// Add managed packet handling for nostale client. + /// + /// + /// Adds a managed packet handler that calls managed packet responders. + /// Adds contractor and contract packet responder. + /// + /// The service collection to register the responder to. + /// The collection. + public static IServiceCollection AddManagedNostaleCore + ( + this IServiceCollection serviceCollection + ) + { + serviceCollection.AddNostaleCore(); + serviceCollection.Replace(ServiceDescriptor.Singleton()); serviceCollection.AddPacketSerialization(); - serviceCollection.AddSingleton(); + serviceCollection.AddTransient(); serviceCollection .AddSingleton() diff --git a/Core/NosSmooth.Core/Packets/IPacketHandler.cs b/Core/NosSmooth.Core/Packets/IPacketHandler.cs new file mode 100644 index 0000000..58fad23 --- /dev/null +++ b/Core/NosSmooth.Core/Packets/IPacketHandler.cs @@ -0,0 +1,36 @@ +// +// IPacketHandler.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.Threading; +using System.Threading.Tasks; +using NosSmooth.Core.Client; +using NosSmooth.Packets; +using NosSmooth.PacketSerializer.Abstractions.Attributes; +using Remora.Results; + +namespace NosSmooth.Core.Packets; + +/// +/// Calls registered responders for the packet that should be handled. +/// +public interface IPacketHandler +{ + /// + /// Calls a responder for the given packet. + /// + /// The current NosTale client. + /// The source of the packet. + /// The string of the packet. + /// The cancellation token for cancelling the operation. + /// A result that may or may not have succeeded. + public Task HandlePacketAsync + ( + INostaleClient client, + PacketSource packetType, + string packetString, + CancellationToken ct = default + ); +} \ No newline at end of file diff --git a/Core/NosSmooth.Core/Packets/IPostExecutionEvent.cs b/Core/NosSmooth.Core/Packets/IPostExecutionEvent.cs index 838b51b..0953c80 100644 --- a/Core/NosSmooth.Core/Packets/IPostExecutionEvent.cs +++ b/Core/NosSmooth.Core/Packets/IPostExecutionEvent.cs @@ -35,4 +35,20 @@ public interface IPostExecutionEvent CancellationToken ct = default ) where TPacket : IPacket; + + /// + /// Execute the post execution event. + /// + /// The NosTale client. + /// The packet arguments. + /// The results from the packet responders. + /// The cancellation token for cancelling the operation. + /// A result that may or may not succeed. + public Task ExecuteAfterExecutionAsync + ( + INostaleClient client, + PacketEventArgs packetArgs, + IReadOnlyList executionResults, + CancellationToken ct = default + ); } \ No newline at end of file diff --git a/Core/NosSmooth.Core/Packets/IPreExecutionEvent.cs b/Core/NosSmooth.Core/Packets/IPreExecutionEvent.cs index 8d8fb50..6255686 100644 --- a/Core/NosSmooth.Core/Packets/IPreExecutionEvent.cs +++ b/Core/NosSmooth.Core/Packets/IPreExecutionEvent.cs @@ -35,4 +35,21 @@ public interface IPreExecutionEvent CancellationToken ct = default ) where TPacket : IPacket; + + /// + /// Execute the pre execution event. + /// + /// + /// If an error is retuned, the packet responders won't be called. + /// + /// The NosTale client. + /// The packet arguments. + /// The cancellation token for cancelling the operation. + /// A result that may or may not succeed. + public Task ExecuteBeforeExecutionAsync + ( + INostaleClient client, + PacketEventArgs packetArgs, + CancellationToken ct = default + ); } \ No newline at end of file diff --git a/Core/NosSmooth.Core/Packets/IRawPacketResponder.cs b/Core/NosSmooth.Core/Packets/IRawPacketResponder.cs new file mode 100644 index 0000000..9f2eadd --- /dev/null +++ b/Core/NosSmooth.Core/Packets/IRawPacketResponder.cs @@ -0,0 +1,26 @@ +// +// IRawPacketResponder.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.Threading; +using System.Threading.Tasks; +using Remora.Results; + +namespace NosSmooth.Core.Packets; + +/// +/// Represents interface for classes that respond to packets. +/// Responds to a raw packet string. +/// +public interface IRawPacketResponder +{ + /// + /// Respond to the given packet. + /// + /// The packet to respond to. + /// The cancellation token for cancelling the operation. + /// A result that may or may not have succeeded. + public Task Respond(PacketEventArgs packetArgs, CancellationToken ct = default); +} \ No newline at end of file diff --git a/Core/NosSmooth.Core/Packets/PacketHandler.cs b/Core/NosSmooth.Core/Packets/ManagedPacketHandler.cs similarity index 61% rename from Core/NosSmooth.Core/Packets/PacketHandler.cs rename to Core/NosSmooth.Core/Packets/ManagedPacketHandler.cs index 89a72d9..e914db6 100644 --- a/Core/NosSmooth.Core/Packets/PacketHandler.cs +++ b/Core/NosSmooth.Core/Packets/ManagedPacketHandler.cs @@ -1,5 +1,5 @@ -// -// PacketHandler.cs +// +// ManagedPacketHandler.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. @@ -11,27 +11,98 @@ using System.Reflection; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; using NosSmooth.Core.Client; +using NosSmooth.Core.Extensions; using NosSmooth.Packets; +using NosSmooth.PacketSerializer; using NosSmooth.PacketSerializer.Abstractions.Attributes; +using NosSmooth.PacketSerializer.Errors; using Remora.Results; namespace NosSmooth.Core.Packets; /// -/// Calls registered responders for the packet that should be handled. +/// Calls IRawPacketResponder and IPacketResponder{T}. /// -public class PacketHandler +public class ManagedPacketHandler : IPacketHandler { - private readonly IServiceProvider _provider; + private readonly IServiceProvider _services; + private readonly IPacketSerializer _packetSerializer; + private readonly ILogger _logger; + private readonly IPacketHandler _rawPacketHandler; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - /// The dependency injection provider. - public PacketHandler(IServiceProvider provider) + /// The service provider. + /// The packet serializer. + /// The logger. + public ManagedPacketHandler + (IServiceProvider services, IPacketSerializer packetSerializer, ILogger logger) { - _provider = provider; + _rawPacketHandler = new RawPacketHandler(services); + _services = services; + _packetSerializer = packetSerializer; + _logger = logger; + } + + /// + public async Task HandlePacketAsync + ( + INostaleClient client, + PacketSource packetType, + string packetString, + CancellationToken ct = default + ) + { + var rawResult = await _rawPacketHandler.HandlePacketAsync(client, packetType, packetString, ct); + + IPacket packet; + var deserializedResult = _packetSerializer.Deserialize(packetString, packetType); + if (!deserializedResult.IsDefined(out var _)) + { + if (deserializedResult.Error is not PacketConverterNotFoundError) + { + _logger.LogWarning("Could not parse {Packet}. Reason:", packetString); + _logger.LogResultError(deserializedResult); + packet = new ParsingFailedPacket(deserializedResult, packetString); + } + else + { + packet = new UnresolvedPacket(packetString.Split(' ')[0], packetString); + } + } + else + { + packet = deserializedResult.Entity; + } + + var managedResult = await HandlePacketAsync + ( + client, + packetType, + packet, + packetString, + ct + ); + + if (!rawResult.IsSuccess && !managedResult.IsSuccess) + { + return new AggregateError(rawResult, managedResult); + } + + if (!rawResult.IsSuccess) + { + return rawResult; + } + + if (!managedResult.IsSuccess) + { + return managedResult; + } + + return Result.FromSuccess(); } /// @@ -43,7 +114,7 @@ public class PacketHandler /// The string of the packet. /// The cancellation token for cancelling the operation. /// A result that may or may not have succeeded. - public Task HandlePacketAsync + private Task HandlePacketAsync ( INostaleClient client, PacketSource packetType, @@ -88,7 +159,7 @@ public class PacketHandler ) where TPacket : class, IPacket { - using var scope = _provider.CreateScope(); + using var scope = _services.CreateScope(); var packetEventArgs = new PacketEventArgs(packetType, packet, packetString); var preExecutionResult = await ExecuteBeforeExecutionAsync(scope.ServiceProvider, client, packetEventArgs, ct); @@ -103,8 +174,10 @@ public class PacketHandler Result[] results; try { - var tasks = packetResponders.Select(responder => responder.Respond(packetEventArgs, ct)).ToList(); - tasks.AddRange(genericPacketResponders.Select(responder => responder.Respond(packetEventArgs, ct))); + var tasks = packetResponders.Select + (responder => SafeCall(() => responder.Respond(packetEventArgs, ct))).ToList(); + tasks.AddRange + (genericPacketResponders.Select(responder => SafeCall(() => responder.Respond(packetEventArgs, ct)))); results = await Task.WhenAll(tasks); } @@ -157,7 +230,7 @@ public class PacketHandler var results = await Task.WhenAll ( services.GetServices() - .Select(x => x.ExecuteBeforeExecutionAsync(client, eventArgs, ct)) + .Select(x => SafeCall(() => x.ExecuteBeforeExecutionAsync(client, eventArgs, ct))) ); var errorResults = new List(); @@ -197,7 +270,7 @@ public class PacketHandler var results = await Task.WhenAll ( services.GetServices() - .Select(x => x.ExecuteAfterExecutionAsync(client, eventArgs, executionResults, ct)) + .Select(x => SafeCall(() => x.ExecuteAfterExecutionAsync(client, eventArgs, executionResults, ct))) ); var errorResults = new List(); @@ -221,4 +294,16 @@ public class PacketHandler return e; } } + + private async Task SafeCall(Func> task) + { + try + { + return await task(); + } + catch (Exception e) + { + return e; + } + } } \ No newline at end of file diff --git a/Core/NosSmooth.Core/Packets/PacketEventArgs.cs b/Core/NosSmooth.Core/Packets/PacketEventArgs.cs index 2020324..2cc53e8 100644 --- a/Core/NosSmooth.Core/Packets/PacketEventArgs.cs +++ b/Core/NosSmooth.Core/Packets/PacketEventArgs.cs @@ -8,6 +8,13 @@ using NosSmooth.PacketSerializer.Abstractions.Attributes; namespace NosSmooth.Core.Packets; +/// +/// Arguments for , . +/// +/// The source of the packet. +/// The packet string. +public record PacketEventArgs(PacketSource Source, string PacketString); + /// /// Arguments for /// diff --git a/Core/NosSmooth.Core/Packets/RawPacketHandler.cs b/Core/NosSmooth.Core/Packets/RawPacketHandler.cs new file mode 100644 index 0000000..fdc2cb8 --- /dev/null +++ b/Core/NosSmooth.Core/Packets/RawPacketHandler.cs @@ -0,0 +1,188 @@ +// +// RawPacketHandler.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 System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using NosSmooth.Core.Client; +using NosSmooth.Packets; +using NosSmooth.PacketSerializer.Abstractions.Attributes; +using Remora.Results; + +namespace NosSmooth.Core.Packets; + +/// +/// Calls IRawPacketResponder. +/// +public class RawPacketHandler : IPacketHandler +{ + private readonly IServiceProvider _services; + + /// + /// Initializes a new instance of the class. + /// + /// The serivce provider. + public RawPacketHandler(IServiceProvider services) + { + _services = services; + + } + + /// + public async Task HandlePacketAsync + ( + INostaleClient client, + PacketSource packetType, + string packetString, + CancellationToken ct = default + ) + { + using var scope = _services.CreateScope(); + var packetEventArgs = new PacketEventArgs(packetType, packetString); + + var preExecutionResult = await ExecuteBeforeExecutionAsync(scope.ServiceProvider, client, packetEventArgs, ct); + if (!preExecutionResult.IsSuccess) + { + return preExecutionResult; + } + + var packetResponders = scope.ServiceProvider.GetServices(); + + Result[] results; + try + { + var tasks = packetResponders.Select + (responder => SafeCall(() => responder.Respond(packetEventArgs, ct))).ToList(); + + results = await Task.WhenAll(tasks); + } + catch (Exception e) + { + results = new Result[] { e }; + } + + var errors = new List(); + foreach (var result in results) + { + if (!result.IsSuccess) + { + errors.Add(result); + } + } + + var postExecutionResult = await ExecuteAfterExecutionAsync + ( + scope.ServiceProvider, + client, + packetEventArgs, + results, + ct + ); + if (!postExecutionResult.IsSuccess) + { + errors.Add(postExecutionResult); + } + + return errors.Count switch + { + 0 => Result.FromSuccess(), + 1 => errors[0], + _ => new AggregateError(errors.Cast().ToArray()) + }; + } + + private async Task ExecuteBeforeExecutionAsync + ( + IServiceProvider services, + INostaleClient client, + PacketEventArgs eventArgs, + CancellationToken ct + ) + { + try + { + var results = await Task.WhenAll + ( + services.GetServices() + .Select(x => SafeCall(() => x.ExecuteBeforeExecutionAsync(client, eventArgs, ct))) + ); + + var errorResults = new List(); + foreach (var result in results) + { + if (!result.IsSuccess) + { + errorResults.Add(result); + } + } + + return errorResults.Count switch + { + 1 => errorResults[0], + 0 => Result.FromSuccess(), + _ => new AggregateError(errorResults.Cast().ToArray()) + }; + } + catch (Exception e) + { + return e; + } + } + + private async Task ExecuteAfterExecutionAsync + ( + IServiceProvider services, + INostaleClient client, + PacketEventArgs eventArgs, + IReadOnlyList executionResults, + CancellationToken ct + ) + { + try + { + var results = await Task.WhenAll + ( + services.GetServices() + .Select(x => SafeCall(() => x.ExecuteAfterExecutionAsync(client, eventArgs, executionResults, ct))) + ); + + var errorResults = new List(); + foreach (var result in results) + { + if (!result.IsSuccess) + { + errorResults.Add(result); + } + } + + return errorResults.Count switch + { + 1 => errorResults[0], + 0 => Result.FromSuccess(), + _ => new AggregateError(errorResults.Cast().ToArray()) + }; + } + catch (Exception e) + { + return e; + } + } + + private async Task SafeCall(Func> task) + { + try + { + return await task(); + } + catch (Exception e) + { + return e; + } + } +} \ No newline at end of file diff --git a/Core/NosSmooth.Core/Stateful/StatefulPreExecutionEvent.cs b/Core/NosSmooth.Core/Stateful/StatefulPreExecutionEvent.cs index cbd2826..0a9cf9c 100644 --- a/Core/NosSmooth.Core/Stateful/StatefulPreExecutionEvent.cs +++ b/Core/NosSmooth.Core/Stateful/StatefulPreExecutionEvent.cs @@ -41,8 +41,17 @@ internal class StatefulPreExecutionEvent : IPreExecutionEvent, IPreCommandExecut return Task.FromResult(Result.FromSuccess()); } + /// + public Task ExecuteBeforeExecutionAsync + (INostaleClient client, PacketEventArgs packetArgs, CancellationToken ct = default) + { + _injector.Client = client; + return Task.FromResult(Result.FromSuccess()); + } + /// - public Task ExecuteBeforeCommandAsync(INostaleClient client, TCommand command, CancellationToken ct = default) + public Task ExecuteBeforeCommandAsync + (INostaleClient client, TCommand command, CancellationToken ct = default) where TCommand : ICommand { _injector.Client = client; diff --git a/Core/NosSmooth.Game/Apis/Safe/NostaleChatApi.cs b/Core/NosSmooth.Game/Apis/Safe/NostaleChatApi.cs index a8e1a61..0d6a1d0 100644 --- a/Core/NosSmooth.Game/Apis/Safe/NostaleChatApi.cs +++ b/Core/NosSmooth.Game/Apis/Safe/NostaleChatApi.cs @@ -18,13 +18,13 @@ namespace NosSmooth.Game.Apis.Safe; public class NostaleChatApi { // TODO: check length of the messages - private readonly INostaleClient _client; + private readonly ManagedNostaleClient _client; /// /// Initializes a new instance of the class. /// /// The nostale client. - public NostaleChatApi(INostaleClient client) + public NostaleChatApi(ManagedNostaleClient client) { _client = client; } diff --git a/Core/NosSmooth.Game/Apis/Safe/NostaleSkillsApi.cs b/Core/NosSmooth.Game/Apis/Safe/NostaleSkillsApi.cs index ea6a2af..d9bc53a 100644 --- a/Core/NosSmooth.Game/Apis/Safe/NostaleSkillsApi.cs +++ b/Core/NosSmooth.Game/Apis/Safe/NostaleSkillsApi.cs @@ -25,7 +25,7 @@ namespace NosSmooth.Game.Apis.Safe; public class NostaleSkillsApi { private readonly Game _game; - private readonly INostaleClient _client; + private readonly ManagedNostaleClient _client; private readonly Contractor _contractor; /// @@ -34,7 +34,7 @@ public class NostaleSkillsApi /// The game. /// The NosTale client. /// The contractor. - public NostaleSkillsApi(Game game, INostaleClient client, Contractor contractor) + public NostaleSkillsApi(Game game, ManagedNostaleClient client, Contractor contractor) { _game = game; _client = client; diff --git a/Core/NosSmooth.Game/Apis/Unsafe/UnsafeInventoryApi.cs b/Core/NosSmooth.Game/Apis/Unsafe/UnsafeInventoryApi.cs index 7fedd11..659c1f2 100644 --- a/Core/NosSmooth.Game/Apis/Unsafe/UnsafeInventoryApi.cs +++ b/Core/NosSmooth.Game/Apis/Unsafe/UnsafeInventoryApi.cs @@ -23,7 +23,7 @@ namespace NosSmooth.Game.Apis.Unsafe; /// public class UnsafeInventoryApi { - private readonly INostaleClient _client; + private readonly ManagedNostaleClient _client; private readonly Contractor _contractor; /// @@ -31,7 +31,7 @@ public class UnsafeInventoryApi /// /// The nostale client. /// The contractor. - public UnsafeInventoryApi(INostaleClient client, Contractor contractor) + public UnsafeInventoryApi(ManagedNostaleClient client, Contractor contractor) { _client = client; _contractor = contractor; diff --git a/Core/NosSmooth.Game/Apis/Unsafe/UnsafeMapApi.cs b/Core/NosSmooth.Game/Apis/Unsafe/UnsafeMapApi.cs index 64a6033..2be88af 100644 --- a/Core/NosSmooth.Game/Apis/Unsafe/UnsafeMapApi.cs +++ b/Core/NosSmooth.Game/Apis/Unsafe/UnsafeMapApi.cs @@ -19,14 +19,14 @@ namespace NosSmooth.Game.Apis.Unsafe; public class UnsafeMapApi { private readonly Game _game; - private readonly INostaleClient _client; + private readonly ManagedNostaleClient _client; /// /// Initializes a new instance of the class. /// /// The game. /// The client. - public UnsafeMapApi(Game game, INostaleClient client) + public UnsafeMapApi(Game game, ManagedNostaleClient client) { _game = game; _client = client; diff --git a/Core/NosSmooth.Game/Apis/Unsafe/UnsafeMateApi.cs b/Core/NosSmooth.Game/Apis/Unsafe/UnsafeMateApi.cs index 194705e..3e65a96 100644 --- a/Core/NosSmooth.Game/Apis/Unsafe/UnsafeMateApi.cs +++ b/Core/NosSmooth.Game/Apis/Unsafe/UnsafeMateApi.cs @@ -17,13 +17,13 @@ namespace NosSmooth.Game.Apis.Unsafe; /// public class UnsafeMateApi { - private readonly INostaleClient _client; + private readonly ManagedNostaleClient _client; /// /// Initializes a new instance of the class. /// /// The client. - public UnsafeMateApi(INostaleClient client) + public UnsafeMateApi(ManagedNostaleClient client) { _client = client; } diff --git a/Core/NosSmooth.Game/Apis/Unsafe/UnsafeMateSkillsApi.cs b/Core/NosSmooth.Game/Apis/Unsafe/UnsafeMateSkillsApi.cs index efbe7f3..aca8c5a 100644 --- a/Core/NosSmooth.Game/Apis/Unsafe/UnsafeMateSkillsApi.cs +++ b/Core/NosSmooth.Game/Apis/Unsafe/UnsafeMateSkillsApi.cs @@ -16,13 +16,13 @@ namespace NosSmooth.Game.Apis.Unsafe; /// public class UnsafeMateSkillsApi { - private readonly INostaleClient _client; + private readonly ManagedNostaleClient _client; /// /// Initializes a new instance of the class. /// /// The client. - public UnsafeMateSkillsApi(INostaleClient client) + public UnsafeMateSkillsApi(ManagedNostaleClient client) { _client = client; } diff --git a/Core/NosSmooth.Game/Apis/Unsafe/UnsafeSkillsApi.cs b/Core/NosSmooth.Game/Apis/Unsafe/UnsafeSkillsApi.cs index 8b6c585..9babcd0 100644 --- a/Core/NosSmooth.Game/Apis/Unsafe/UnsafeSkillsApi.cs +++ b/Core/NosSmooth.Game/Apis/Unsafe/UnsafeSkillsApi.cs @@ -23,7 +23,7 @@ namespace NosSmooth.Game.Apis.Unsafe; /// public class UnsafeSkillsApi { - private readonly INostaleClient _client; + private readonly ManagedNostaleClient _client; private readonly Game _game; private readonly Contractor _contractor; @@ -33,7 +33,7 @@ public class UnsafeSkillsApi /// The nostale client. /// The game. /// The contractor. - public UnsafeSkillsApi(INostaleClient client, Game game, Contractor contractor) + public UnsafeSkillsApi(ManagedNostaleClient client, Game game, Contractor contractor) { _client = client; _game = game; diff --git a/Core/NosSmooth.Game/Extensions/ServiceCollectionExtensions.cs b/Core/NosSmooth.Game/Extensions/ServiceCollectionExtensions.cs index a742d58..e4c6ec2 100644 --- a/Core/NosSmooth.Game/Extensions/ServiceCollectionExtensions.cs +++ b/Core/NosSmooth.Game/Extensions/ServiceCollectionExtensions.cs @@ -41,7 +41,7 @@ public static class ServiceCollectionExtensions public static IServiceCollection AddNostaleGame(this IServiceCollection serviceCollection) { serviceCollection - .AddNostaleCore() + .AddManagedNostaleCore() .AddMemoryCache() .TryAddScoped(); serviceCollection.TryAddSingleton(); diff --git a/Extensions/NosSmooth.Extensions.Combat/CombatManager.cs b/Extensions/NosSmooth.Extensions.Combat/CombatManager.cs index 46be264..d84c82a 100644 --- a/Extensions/NosSmooth.Extensions.Combat/CombatManager.cs +++ b/Extensions/NosSmooth.Extensions.Combat/CombatManager.cs @@ -20,7 +20,7 @@ namespace NosSmooth.Extensions.Combat; /// public class CombatManager : IStatefulEntity { - private readonly INostaleClient _client; + private readonly ManagedNostaleClient _client; private readonly Game.Game _game; /// @@ -28,7 +28,7 @@ public class CombatManager : IStatefulEntity /// /// The NosTale client. /// The game. - public CombatManager(INostaleClient client, Game.Game game) + public CombatManager(ManagedNostaleClient client, Game.Game game) { _client = client; _game = game; diff --git a/Extensions/NosSmooth.Extensions.Combat/CombatState.cs b/Extensions/NosSmooth.Extensions.Combat/CombatState.cs index e1c482b..f73e313 100644 --- a/Extensions/NosSmooth.Extensions.Combat/CombatState.cs +++ b/Extensions/NosSmooth.Extensions.Combat/CombatState.cs @@ -22,7 +22,7 @@ internal class CombatState : ICombatState /// The NosTale client. /// The game. /// The combat manager. - public CombatState(INostaleClient client, Game.Game game, CombatManager combatManager) + public CombatState(ManagedNostaleClient client, Game.Game game, CombatManager combatManager) { Client = client; Game = game; @@ -43,7 +43,7 @@ internal class CombatState : ICombatState public Game.Game Game { get; } /// - public INostaleClient Client { get; } + public ManagedNostaleClient Client { get; } /// /// Gets whether the manager may currently quit. diff --git a/Extensions/NosSmooth.Extensions.Combat/ICombatState.cs b/Extensions/NosSmooth.Extensions.Combat/ICombatState.cs index b25ea61..a3172dd 100644 --- a/Extensions/NosSmooth.Extensions.Combat/ICombatState.cs +++ b/Extensions/NosSmooth.Extensions.Combat/ICombatState.cs @@ -29,7 +29,7 @@ public interface ICombatState /// /// Gets the NosTale client. /// - public INostaleClient Client { get; } + public ManagedNostaleClient Client { get; } /// /// Gets whether there is an operation that cannot be used diff --git a/Samples/FileClient/Client.cs b/Samples/FileClient/Client.cs index f2243eb..ebe1a04 100644 --- a/Samples/FileClient/Client.cs +++ b/Samples/FileClient/Client.cs @@ -24,8 +24,7 @@ namespace FileClient; public class Client : BaseNostaleClient { private const string LineRegex = ".*\\[(Recv|Send)\\]\t(.*)"; - private readonly PacketHandler _packetHandler; - private readonly IPacketSerializer _packetSerializer; + private readonly IPacketHandler _packetHandler; private readonly ILogger _logger; private readonly Stream _stream; @@ -35,21 +34,18 @@ public class Client : BaseNostaleClient /// The stream with packets. /// The packet handler. /// The command processor. - /// The packet serializer. /// The logger. public Client ( Stream stream, - PacketHandler packetHandler, + IPacketHandler packetHandler, CommandProcessor commandProcessor, - IPacketSerializer packetSerializer, ILogger logger ) - : base(commandProcessor, packetSerializer) + : base(commandProcessor) { _stream = stream; _packetHandler = packetHandler; - _packetSerializer = packetSerializer; _logger = logger; } @@ -78,12 +74,10 @@ public class Client : BaseNostaleClient var packetStr = match.Groups[2].Value; var source = type == "Recv" ? PacketSource.Server : PacketSource.Client; - var packet = CreatePacket(packetStr, source); Result result = await _packetHandler.HandlePacketAsync ( this, source, - packet, packetStr, stopRequested ); @@ -103,7 +97,6 @@ public class Client : BaseNostaleClient ( this, PacketSource.Client, - CreatePacket(packetString, PacketSource.Client), packetString, ct ); @@ -116,25 +109,8 @@ public class Client : BaseNostaleClient ( this, PacketSource.Server, - CreatePacket(packetString, PacketSource.Server), packetString, ct ); } - - private IPacket CreatePacket(string packetStr, PacketSource source) - { - var packetResult = _packetSerializer.Deserialize(packetStr, source); - if (!packetResult.IsSuccess) - { - if (packetResult.Error is PacketConverterNotFoundError err) - { - return new UnresolvedPacket(err.Header, packetStr); - } - - return new ParsingFailedPacket(packetResult, packetStr); - } - - return packetResult.Entity; - } } \ No newline at end of file diff --git a/Samples/FileClient/Program.cs b/Samples/FileClient/Program.cs index a13ad23..7d3dbfd 100644 --- a/Samples/FileClient/Program.cs +++ b/Samples/FileClient/Program.cs @@ -46,7 +46,7 @@ public static class Program { coll.AddHostedService(); - coll.AddNostaleCore() + coll.AddManagedNostaleCore() .AddNostaleGame() .AddNostaleDataFiles() .AddPacketResponder() @@ -57,9 +57,8 @@ public static class Program }); coll.AddSingleton(p => new Client( fileStream, - p.GetRequiredService(), + p.GetRequiredService(), p.GetRequiredService(), - p.GetRequiredService(), p.GetRequiredService>() )); }) diff --git a/Tests/NosSmooth.Core.Tests/Fakes/FakeEmptyNostaleClient.cs b/Tests/NosSmooth.Core.Tests/Fakes/FakeEmptyNostaleClient.cs index 7cc2e82..f105b56 100644 --- a/Tests/NosSmooth.Core.Tests/Fakes/FakeEmptyNostaleClient.cs +++ b/Tests/NosSmooth.Core.Tests/Fakes/FakeEmptyNostaleClient.cs @@ -23,12 +23,6 @@ public class FakeEmptyNostaleClient : INostaleClient throw new NotImplementedException(); } - /// - public Task SendPacketAsync(IPacket packet, CancellationToken ct = default) - { - throw new NotImplementedException(); - } - /// public Task SendPacketAsync(string packetString, CancellationToken ct = default) { @@ -41,12 +35,6 @@ public class FakeEmptyNostaleClient : INostaleClient throw new NotImplementedException(); } - /// - public Task ReceivePacketAsync(IPacket packet, CancellationToken ct = default) - { - throw new NotImplementedException(); - } - /// public Task SendCommandAsync(ICommand command, CancellationToken ct = default) { diff --git a/Tests/NosSmooth.Core.Tests/Fakes/FakeLogger.cs b/Tests/NosSmooth.Core.Tests/Fakes/FakeLogger.cs new file mode 100644 index 0000000..b1e5c64 --- /dev/null +++ b/Tests/NosSmooth.Core.Tests/Fakes/FakeLogger.cs @@ -0,0 +1,39 @@ +// +// FakeLogger.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.Logging; + +namespace NosSmooth.Core.Tests.Fakes; + +/// +public class FakeLogger : ILogger +{ + /// + public void Log + ( + LogLevel logLevel, + EventId eventId, + TState state, + Exception? exception, + Func formatter + ) + { + } + + /// + public bool IsEnabled(LogLevel logLevel) + { + return true; + } + + /// + public IDisposable? BeginScope(TState state) + where TState : notnull + { + return null; + } +} \ No newline at end of file diff --git a/Tests/NosSmooth.Core.Tests/Fakes/FakeNostaleClient.cs b/Tests/NosSmooth.Core.Tests/Fakes/FakeNostaleClient.cs index d750cde..cc7108b 100644 --- a/Tests/NosSmooth.Core.Tests/Fakes/FakeNostaleClient.cs +++ b/Tests/NosSmooth.Core.Tests/Fakes/FakeNostaleClient.cs @@ -36,12 +36,6 @@ public class FakeNostaleClient : INostaleClient throw new System.NotImplementedException(); } - /// - public Task SendPacketAsync(IPacket packet, CancellationToken ct = default) - { - throw new System.NotImplementedException(); - } - /// public Task SendPacketAsync(string packetString, CancellationToken ct = default) { @@ -54,12 +48,6 @@ public class FakeNostaleClient : INostaleClient throw new System.NotImplementedException(); } - /// - public Task ReceivePacketAsync(IPacket packet, CancellationToken ct = default) - { - throw new System.NotImplementedException(); - } - /// public Task SendCommandAsync(ICommand command, CancellationToken ct = default) => Task.FromResult(_handleCommand(command, ct)); diff --git a/Tests/NosSmooth.Core.Tests/Fakes/Packets/Events/PacketEvent.cs b/Tests/NosSmooth.Core.Tests/Fakes/Packets/Events/PacketEvent.cs index c6684b2..a129ed3 100644 --- a/Tests/NosSmooth.Core.Tests/Fakes/Packets/Events/PacketEvent.cs +++ b/Tests/NosSmooth.Core.Tests/Fakes/Packets/Events/PacketEvent.cs @@ -46,6 +46,11 @@ public class PacketEvent : IPreExecutionEvent, IPostExecutionEvent where TPacket : IPacket => Task.FromResult(_preHandler(client, packetArgs.Source, packetArgs.Packet, packetArgs.PacketString)); + /// + public Task ExecuteBeforeExecutionAsync + (INostaleClient client, PacketEventArgs packetArgs, CancellationToken ct = default) + => Task.FromResult(Result.FromSuccess()); + /// public Task ExecuteAfterExecutionAsync ( @@ -66,4 +71,14 @@ public class PacketEvent : IPreExecutionEvent, IPostExecutionEvent executionResults ) ); + + /// + public Task ExecuteAfterExecutionAsync + ( + INostaleClient client, + PacketEventArgs packetArgs, + IReadOnlyList executionResults, + CancellationToken ct = default + ) + => Task.FromResult(Result.FromSuccess()); } \ No newline at end of file diff --git a/Tests/NosSmooth.Core.Tests/Fakes/Packets/FakePacket.cs b/Tests/NosSmooth.Core.Tests/Fakes/Packets/FakePacket.cs index c9354ed..7a1d6ae 100644 --- a/Tests/NosSmooth.Core.Tests/Fakes/Packets/FakePacket.cs +++ b/Tests/NosSmooth.Core.Tests/Fakes/Packets/FakePacket.cs @@ -5,6 +5,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using NosSmooth.Packets; +using NosSmooth.PacketSerializer.Abstractions.Attributes; namespace NosSmooth.Core.Tests.Fakes.Packets; @@ -12,4 +13,10 @@ namespace NosSmooth.Core.Tests.Fakes.Packets; /// A fake packet. /// /// The input. -public record FakePacket(string Input) : IPacket; \ No newline at end of file +[PacketHeader("fake", PacketSource.Server)] +[GenerateSerializer(true)] +public record FakePacket +( + [PacketGreedyIndex(0)] + string Input +) : IPacket; \ No newline at end of file diff --git a/Tests/NosSmooth.Core.Tests/NosSmooth.Core.Tests.csproj b/Tests/NosSmooth.Core.Tests/NosSmooth.Core.Tests.csproj index 3cf80f0..8cac215 100644 --- a/Tests/NosSmooth.Core.Tests/NosSmooth.Core.Tests.csproj +++ b/Tests/NosSmooth.Core.Tests/NosSmooth.Core.Tests.csproj @@ -1,8 +1,10 @@ - net7.0 + enable + net7.0;netstandard2.1 enable + 10 false @@ -27,4 +29,8 @@ + + + + diff --git a/Tests/NosSmooth.Core.Tests/Packets/PacketHandlerTests.Events.cs b/Tests/NosSmooth.Core.Tests/Packets/PacketHandlerTests.Events.cs index f37f93a..292f3db 100644 --- a/Tests/NosSmooth.Core.Tests/Packets/PacketHandlerTests.Events.cs +++ b/Tests/NosSmooth.Core.Tests/Packets/PacketHandlerTests.Events.cs @@ -5,8 +5,10 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; +using System.Reflection; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; using NosSmooth.Core.Commands; using NosSmooth.Core.Packets; using NosSmooth.Core.Tests.Fakes; @@ -14,6 +16,8 @@ using NosSmooth.Core.Tests.Fakes.Commands; using NosSmooth.Core.Tests.Fakes.Packets; using NosSmooth.Core.Tests.Fakes.Packets.Events; using NosSmooth.PacketSerializer.Abstractions.Attributes; +using NosSmooth.PacketSerializer.Extensions; +using NosSmooth.PacketSerializer.Packets; using Remora.Results; using Xunit; @@ -34,7 +38,10 @@ public class PacketHandlerTestsEvents var called = false; var client = new FakeEmptyNostaleClient(); var provider = new ServiceCollection() - .AddSingleton() + .AddPacketSerialization() + .AddGeneratedSerializers(Assembly.GetExecutingAssembly()) + .AddSingleton(typeof(ILogger<>), typeof(FakeLogger<>)) + .AddSingleton() .AddScoped ( _ => new PacketEvent @@ -59,8 +66,9 @@ public class PacketHandlerTestsEvents ) .BuildServiceProvider(); - var result = await provider.GetRequiredService().HandlePacketAsync - (client, PacketSource.Client, new FakePacket("a"), "fake a"); + provider.GetRequiredService().AddPacketType(typeof(FakePacket)); + var result = await provider.GetRequiredService().HandlePacketAsync + (client, PacketSource.Client, "fake a"); Assert.True(result.IsSuccess); Assert.True(called); } diff --git a/Tests/NosSmooth.Core.Tests/Stateful/StatefulInjectorTests.cs b/Tests/NosSmooth.Core.Tests/Stateful/StatefulInjectorTests.cs index fd5d9d7..fdd2db6 100644 --- a/Tests/NosSmooth.Core.Tests/Stateful/StatefulInjectorTests.cs +++ b/Tests/NosSmooth.Core.Tests/Stateful/StatefulInjectorTests.cs @@ -4,8 +4,10 @@ // 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 System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; using NosSmooth.Core.Client; using NosSmooth.Core.Commands; using NosSmooth.Core.Extensions; @@ -17,6 +19,8 @@ using NosSmooth.Core.Tests.Fakes.Packets; using NosSmooth.Core.Tests.Packets; using NosSmooth.Packets.Server.Maps; using NosSmooth.PacketSerializer.Abstractions.Attributes; +using NosSmooth.PacketSerializer.Extensions; +using NosSmooth.PacketSerializer.Packets; using Remora.Results; using Xunit; @@ -135,7 +139,10 @@ public class StatefulInjectorTests .AddStatefulInjector() .AddStatefulEntity() .AddSingleton() - .AddSingleton() + .AddSingleton(typeof(ILogger<>), typeof(FakeLogger<>)) + .AddPacketSerialization() + .AddGeneratedSerializers(Assembly.GetExecutingAssembly()) + .AddSingleton() .AddScoped> (p => { @@ -161,10 +168,11 @@ public class StatefulInjectorTests ) .BuildServiceProvider(); - var handler = services.GetRequiredService(); + var handler = services.GetRequiredService(); - Assert.True((await handler.HandlePacketAsync(client1, PacketSource.Server, new FakePacket("1"), "fake 1")).IsSuccess); - Assert.True((await handler.HandlePacketAsync(client2, PacketSource.Server, new FakePacket("2"), "fake 2")).IsSuccess); + services.GetRequiredService().AddPacketType(typeof(FakePacket)); + Assert.True((await handler.HandlePacketAsync(client1, PacketSource.Server, "fake 1")).IsSuccess); + Assert.True((await handler.HandlePacketAsync(client2, PacketSource.Server, "fake 2")).IsSuccess); Assert.NotNull(entity1); Assert.NotNull(entity2); Assert.NotEqual(entity1, entity2); -- 2.49.0