using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using Data.Models; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using SharedLibraryCore.Interfaces; using InteractionRegistrationCallback = System.Func>; namespace IW4MAdmin.Application.Misc; public class InteractionRegistration : IInteractionRegistration { private readonly ILogger _logger; private readonly IServiceProvider _serviceProvider; private readonly ConcurrentDictionary _interactions = new(); public InteractionRegistration(ILogger logger, IServiceProvider serviceProvider) { _logger = logger; _serviceProvider = serviceProvider; } public void RegisterScriptInteraction(string interactionName, string source, Delegate interactionRegistration) { var plugin = _serviceProvider.GetRequiredService>() .FirstOrDefault(plugin => plugin.Name == source); if (plugin is not ScriptPlugin scriptPlugin) { return; } var wrappedDelegate = (int? clientId, Reference.Game? game, CancellationToken token) => Task.FromResult( scriptPlugin.WrapDelegate(interactionRegistration, clientId, game, token)); if (!_interactions.ContainsKey(interactionName)) { _interactions.TryAdd(interactionName, wrappedDelegate); } else { _interactions[interactionName] = wrappedDelegate; } } public void RegisterInteraction(string interactionName, InteractionRegistrationCallback interactionRegistration) { if (!_interactions.ContainsKey(interactionName)) { _interactions.TryAdd(interactionName, interactionRegistration); } else { _interactions[interactionName] = interactionRegistration; } } public void UnregisterInteraction(string interactionName) { if (_interactions.ContainsKey(interactionName)) { _interactions.TryRemove(interactionName, out _); } } public async Task> GetInteractions(string interactionPrefix = null, int? clientId = null, Reference.Game? game = null, CancellationToken token = default) { return await GetInteractionsPrivate(interactionPrefix, clientId, game, token); } public async Task ProcessInteraction(string interactionId, int originId, int? targetId = null, Reference.Game? game = null, IDictionary meta = null, CancellationToken token = default) { if (!_interactions.ContainsKey(interactionId)) { throw new ArgumentException($"Interaction with ID {interactionId} has not been registered"); } try { var interaction = await _interactions[interactionId](targetId, game, token); if (interaction.Action is not null) { return await interaction.Action(originId, targetId, game, meta, token); } if (interaction.ScriptAction is not null) { foreach (var plugin in _serviceProvider.GetRequiredService>()) { if (plugin is not ScriptPlugin scriptPlugin || scriptPlugin.Name != interaction.Source) { continue; } return scriptPlugin.ExecuteAction(interaction.ScriptAction, originId, targetId, game, meta, token); } } } catch (Exception ex) { _logger.LogWarning(ex, "Could not process interaction for {InteractionName} and OriginId {ClientId}", interactionId, originId); } return null; } private async Task> GetInteractionsPrivate(string prefix = null, int? clientId = null, Reference.Game? game = null, CancellationToken token = default) { return (await Task.WhenAll(_interactions .Where(interaction => string.IsNullOrWhiteSpace(prefix) || interaction.Key.StartsWith(prefix)).Select( async kvp => { try { return await kvp.Value(clientId, game, token); } catch (Exception ex) { _logger.LogWarning(ex, "Could not get interaction for {InteractionName} and ClientId {ClientId}", kvp.Key, clientId); return null; } }))).Where(interaction => interaction is not null); } }