From 8270c3aeb9db6f8650203b34111be28accc5413e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franti=C5=A1ek=20Boh=C3=A1=C4=8Dek?= Date: Sun, 13 Feb 2022 15:35:24 +0100 Subject: [PATCH] feat(core): add pre and post responder events --- .../Extensions/ServiceCollectionExtensions.cs | 26 +++- .../Converters/ISpecificPacketSerializer.cs | 1 - .../Packets/Converters/InPacketSerializer.cs | 1 - .../Packets/IPostExecutionEvent.cs | 38 ++++++ .../Packets/IPreExecutionEvent.cs | 38 ++++++ Core/NosSmooth.Core/Packets/PacketHandler.cs | 127 ++++++++++++++++-- 6 files changed, 214 insertions(+), 17 deletions(-) delete mode 100644 Core/NosSmooth.Core/Packets/Converters/ISpecificPacketSerializer.cs delete mode 100644 Core/NosSmooth.Core/Packets/Converters/InPacketSerializer.cs create mode 100644 Core/NosSmooth.Core/Packets/IPostExecutionEvent.cs create mode 100644 Core/NosSmooth.Core/Packets/IPreExecutionEvent.cs diff --git a/Core/NosSmooth.Core/Extensions/ServiceCollectionExtensions.cs b/Core/NosSmooth.Core/Extensions/ServiceCollectionExtensions.cs index 434b61e5527f732250bd8a222d4b9e0853cb9e47..611646b6e343553e9ef68264c7b766f5561ed92f 100644 --- a/Core/NosSmooth.Core/Extensions/ServiceCollectionExtensions.cs +++ b/Core/NosSmooth.Core/Extensions/ServiceCollectionExtensions.cs @@ -162,4 +162,28 @@ public static class ServiceCollectionExtensions return serviceCollection; } -} + + /// + /// Add the given pre execution event that will be executed before the packet responders. + /// + /// The service collection. + /// The pre execution event type. + /// The collection. + public static IServiceCollection AddPreExecutionEvent(this IServiceCollection serviceCollection) + where TEvent : class, IPreExecutionEvent + { + return serviceCollection.AddScoped(); + } + + /// + /// Add the given post execution event that will be executed after the packet responders. + /// + /// The service collection. + /// The pre execution event type. + /// The collection. + public static IServiceCollection AddPostExecutionEvent(this IServiceCollection serviceCollection) + where TEvent : class, IPostExecutionEvent + { + return serviceCollection.AddScoped(); + } +} \ No newline at end of file diff --git a/Core/NosSmooth.Core/Packets/Converters/ISpecificPacketSerializer.cs b/Core/NosSmooth.Core/Packets/Converters/ISpecificPacketSerializer.cs deleted file mode 100644 index 5f282702bb03ef11d7184d19c80927b47f919764..0000000000000000000000000000000000000000 --- a/Core/NosSmooth.Core/Packets/Converters/ISpecificPacketSerializer.cs +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/Core/NosSmooth.Core/Packets/Converters/InPacketSerializer.cs b/Core/NosSmooth.Core/Packets/Converters/InPacketSerializer.cs deleted file mode 100644 index 5f282702bb03ef11d7184d19c80927b47f919764..0000000000000000000000000000000000000000 --- a/Core/NosSmooth.Core/Packets/Converters/InPacketSerializer.cs +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/Core/NosSmooth.Core/Packets/IPostExecutionEvent.cs b/Core/NosSmooth.Core/Packets/IPostExecutionEvent.cs new file mode 100644 index 0000000000000000000000000000000000000000..838b51b9d7d6a32cc98591bb99f8c9ee725e2e47 --- /dev/null +++ b/Core/NosSmooth.Core/Packets/IPostExecutionEvent.cs @@ -0,0 +1,38 @@ +// +// IPostExecutionEvent.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.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using NosSmooth.Core.Client; +using NosSmooth.Packets; +using Remora.Results; + +namespace NosSmooth.Core.Packets; + +/// +/// Event executed after the packet responders. +/// +public interface IPostExecutionEvent +{ + /// + /// Execute the post execution event. + /// + /// The NosTale client. + /// The packet arguments. + /// The results from the packet responders. + /// The cancellation token for cancelling the operation. + /// The type of the packet. + /// A result that may or may not succeed. + public Task ExecuteAfterExecutionAsync + ( + INostaleClient client, + PacketEventArgs packetArgs, + IReadOnlyList executionResults, + CancellationToken ct = default + ) + where TPacket : IPacket; +} \ No newline at end of file diff --git a/Core/NosSmooth.Core/Packets/IPreExecutionEvent.cs b/Core/NosSmooth.Core/Packets/IPreExecutionEvent.cs new file mode 100644 index 0000000000000000000000000000000000000000..8d8fb509f36d53e2511732f1039a18c789d476a8 --- /dev/null +++ b/Core/NosSmooth.Core/Packets/IPreExecutionEvent.cs @@ -0,0 +1,38 @@ +// +// IPreExecutionEvent.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 Remora.Results; + +namespace NosSmooth.Core.Packets; + +/// +/// Event executed prior to packet responders. +/// +public interface IPreExecutionEvent +{ + /// + /// 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. + /// The type of the packet. + /// A result that may or may not succeed. + public Task ExecuteBeforeExecutionAsync + ( + INostaleClient client, + PacketEventArgs packetArgs, + CancellationToken ct = default + ) + where TPacket : IPacket; +} \ No newline at end of file diff --git a/Core/NosSmooth.Core/Packets/PacketHandler.cs b/Core/NosSmooth.Core/Packets/PacketHandler.cs index 56e91eaf315db99308669506815642a106815b75..589a3b79dbdc414dd6e1c0cf8c3213be9d49d5b4 100644 --- a/Core/NosSmooth.Core/Packets/PacketHandler.cs +++ b/Core/NosSmooth.Core/Packets/PacketHandler.cs @@ -11,6 +11,7 @@ using System.Reflection; 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; @@ -32,15 +33,28 @@ public class PacketHandler : IPacketHandler } /// - public Task HandleReceivedPacketAsync(IPacket packet, string packetString, CancellationToken ct) - => HandlePacketAsync(PacketSource.Server, packet, packetString, ct); + public Task HandleReceivedPacketAsync + ( + INostaleClient client, + IPacket packet, + string packetString, + CancellationToken ct + ) + => HandlePacketAsync(client, PacketSource.Server, packet, packetString, ct); /// - public Task HandleSentPacketAsync(IPacket packet, string packetString, CancellationToken ct) - => HandlePacketAsync(PacketSource.Client, packet, packetString, ct); + public Task HandleSentPacketAsync + ( + INostaleClient client, + IPacket packet, + string packetString, + CancellationToken ct + ) + => HandlePacketAsync(client, PacketSource.Client, packet, packetString, ct); private Task HandlePacketAsync ( + INostaleClient client, PacketSource packetType, IPacket packet, string packetString, @@ -59,16 +73,23 @@ public class PacketHandler : IPacketHandler } var boundProcessMethod = processMethod.MakeGenericMethod(packet.GetType()); - return (Task)boundProcessMethod.Invoke(this, new object[] - { - packetType, - packet, - packetString, - ct - })!; + return (Task)boundProcessMethod.Invoke + ( + this, + new object[] + { + client, + packetType, + packet, + packetString, + ct + } + )!; } - private async Task DispatchResponder( + private async Task DispatchResponder + ( + INostaleClient client, PacketSource packetType, TPacket packet, string packetString, @@ -77,10 +98,17 @@ public class PacketHandler : IPacketHandler where TPacket : class, IPacket { using var scope = _provider.CreateScope(); + var packetEventArgs = new PacketEventArgs(packetType, packet, packetString); + + var preExecutionResult = await ExecuteBeforeExecutionAsync(scope.ServiceProvider, client, packetEventArgs, ct); + if (!preExecutionResult.IsSuccess) + { + return preExecutionResult; + } + var packetResponders = scope.ServiceProvider.GetServices>(); var genericPacketResponders = scope.ServiceProvider.GetServices(); - var packetEventArgs = new PacketEventArgs(packetType, packet, packetString); var tasks = packetResponders.Select(responder => responder.Respond(packetEventArgs, ct)).ToList(); tasks.AddRange(genericPacketResponders.Select(responder => responder.Respond(packetEventArgs, ct))); @@ -95,6 +123,12 @@ public class PacketHandler : IPacketHandler } } + var postExecutionResult = await ExecuteAfterExecutionAsync(scope.ServiceProvider, client, packetEventArgs, results, ct); + if (!postExecutionResult.IsSuccess) + { + errors.Add(postExecutionResult); + } + return errors.Count switch { 0 => Result.FromSuccess(), @@ -102,4 +136,69 @@ public class PacketHandler : IPacketHandler _ => new AggregateError(errors.Cast().ToArray()) }; } -} + + private async Task ExecuteBeforeExecutionAsync + ( + IServiceProvider services, + INostaleClient client, + PacketEventArgs eventArgs, + CancellationToken ct + ) + where TPacket : IPacket + { + var results = await Task.WhenAll + ( + services.GetServices() + .Select(x => 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()) + }; + } + + private async Task ExecuteAfterExecutionAsync + ( + IServiceProvider services, + INostaleClient client, + PacketEventArgs eventArgs, + IReadOnlyList executionResults, + CancellationToken ct + ) + where TPacket : IPacket + { + var results = await Task.WhenAll + ( + services.GetServices() + .Select(x => 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()) + }; + } +} \ No newline at end of file