// // ServiceCollectionExtensions.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.Linq; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using NosSmooth.Core.Client; using NosSmooth.Core.Commands; using NosSmooth.Core.Commands.Control; using NosSmooth.Core.Commands.Walking; using NosSmooth.Core.Packets; using NosSmooth.Core.Stateful; using NosSmooth.PacketSerializer.Extensions; namespace NosSmooth.Core.Extensions; /// /// Contains extension methods for . /// public static class ServiceCollectionExtensions { /// /// Adds base packet and command handling for nostale client. /// /// The service collection to register the responder to. /// The collection. public static IServiceCollection AddNostaleCore ( this IServiceCollection serviceCollection ) { serviceCollection .TryAddSingleton(); serviceCollection.AddPacketSerialization(); serviceCollection.AddSingleton(); return serviceCollection; } /// /// Adds command handling of and . /// /// The service collection to register the responder to. /// The collection. public static IServiceCollection AddTakeControlCommand(this IServiceCollection serviceCollection) { return serviceCollection .AddSingleton() .AddPacketResponder() .AddCommandHandler() .AddCommandHandler(); } /// /// Adds the specified packet responder that will be called upon receiving the given event. /// /// The service collection to register the responder to. /// The type of the responder. /// Thrown if the type of the responder is incorrect. /// The collection. public static IServiceCollection AddPacketResponder ( this IServiceCollection serviceCollection ) where TPacketResponder : class, IPacketResponder { return serviceCollection.AddPacketResponder(typeof(TPacketResponder)); } /// /// Adds the specified packet responder that will be called upon receiving the given event. /// /// The service collection to register the responder to. /// The type of the responder. /// The collection. /// Thrown if the type of the responder is incorrect. public static IServiceCollection AddPacketResponder ( this IServiceCollection serviceCollection, Type responderType ) { if (responderType.GetInterfaces().Any(i => i == typeof(IEveryPacketResponder))) { return serviceCollection.AddScoped(typeof(IEveryPacketResponder), responderType); } if (!responderType.GetInterfaces().Any ( i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IPacketResponder<>) )) { throw new ArgumentException ( $"{nameof(responderType)} should implement IPacketResponder.", nameof(responderType) ); } var responderTypeInterfaces = responderType.GetInterfaces(); var responderInterfaces = responderTypeInterfaces.Where ( r => r.IsGenericType && r.GetGenericTypeDefinition() == typeof(IPacketResponder<>) ); foreach (var responderInterface in responderInterfaces) { serviceCollection.AddScoped(responderInterface, responderType); } return serviceCollection; } /// /// Adds the specified command handler. /// /// The service collection to register the responder to. /// The type of the command. /// Thrown if the type of the responder is incorrect. /// The collection. public static IServiceCollection AddCommandHandler ( this IServiceCollection serviceCollection ) where TCommandHandler : class, ICommandHandler { return serviceCollection.AddCommandHandler(typeof(TCommandHandler)); } /// /// Adds the specified command handler. /// /// The service collection to register the responder to. /// The type of the command handler. /// The collection. /// Thrown if the type of the responder is incorrect. public static IServiceCollection AddCommandHandler ( this IServiceCollection serviceCollection, Type commandHandlerType ) { if (!commandHandlerType.GetInterfaces().Any ( i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(ICommandHandler<>) )) { throw new ArgumentException ( $"{nameof(commandHandlerType)} should implement ICommandHandler.", nameof(commandHandlerType) ); } var handlerTypeInterfaces = commandHandlerType.GetInterfaces(); var handlerInterfaces = handlerTypeInterfaces.Where ( r => r.IsGenericType && r.GetGenericTypeDefinition() == typeof(ICommandHandler<>) ); foreach (var handlerInterface in handlerInterfaces) { serviceCollection.AddScoped(handlerInterface, commandHandlerType); } return serviceCollection; } /// /// Add the given pre execution event that will be executed before the command handler. /// /// The service collection. /// The pre execution event type. /// The collection. public static IServiceCollection AddPreCommandExecutionEvent(this IServiceCollection serviceCollection) where TEvent : class, IPreCommandExecutionEvent { return serviceCollection.AddScoped(); } /// /// Add the given post execution event that will be executed after the command handler. /// /// The service collection. /// The pre execution event type. /// The collection. public static IServiceCollection AddPostCommandExecutionEvent(this IServiceCollection serviceCollection) where TEvent : class, IPostCommandExecutionEvent { return serviceCollection.AddScoped(); } /// /// 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(); } /// /// Add the injector for stateful entities that may be replaced for different NosTale clients. /// /// The service collection. /// The collection. public static IServiceCollection AddStatefulInjector(this IServiceCollection serviceCollection) { serviceCollection.RemoveAll(); return serviceCollection .AddScoped() .AddSingleton() .AddPreExecutionEvent() .AddPreCommandExecutionEvent() .AddScoped ( p => { var nostaleClient = p.GetRequiredService().Client; if (nostaleClient == null) { throw new NullReferenceException("The client cannot be null."); } return nostaleClient; } ) .ReplaceStatefulEntities(); } /// /// Replace all the stateful entities that are added in the collection. /// /// The service collection. /// The collection. public static IServiceCollection ReplaceStatefulEntities(this IServiceCollection serviceCollection) { foreach (var serviceDescriptor in serviceCollection) { var type = serviceDescriptor.ServiceType; if (typeof(IStatefulEntity).IsAssignableFrom(type)) { serviceCollection.AddStatefulEntity(type); } } return serviceCollection; } /// /// Add a stateful entity of the given type to be injectable into the scope of a client. /// /// The service collection. /// The type of the stateful entity. /// The collection. public static IServiceCollection AddStatefulEntity(this IServiceCollection serviceCollection) => serviceCollection.AddStatefulEntity(typeof(TEntity)); /// /// Add a stateful entity of the given type to be injectable into the scope of a client. /// /// The service collection. /// The type of the stateful entity. /// The collection. public static IServiceCollection AddStatefulEntity (this IServiceCollection serviceCollection, Type statefulEntityType) { serviceCollection.AddStatefulInjector(); serviceCollection.RemoveAll(statefulEntityType); serviceCollection.Add ( ServiceDescriptor.Scoped (statefulEntityType, (p) => p.GetRequiredService().GetEntity(p, statefulEntityType)) ); return serviceCollection; } }