using System; using System.Linq; using System.Reflection; using Godot; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using OwofGames.GodotLume; namespace GodotHostTest.GodotDI; [Icon("res://addons/GodotDI/ServiceSource.svg")] public partial class ServiceSource : Node { private static readonly string RootScopeQualifiedName = typeof(RootScope).AssemblyQualifiedName!; [Export] private string _serviceScope = RootScopeQualifiedName; [Export] protected Node?[] InjectedNodes = []; internal void ResolveInjectedNodes(IServiceProvider serviceProvider, Delegate? onScopeCreated) { var logger = serviceProvider.GetRequiredService>(); // if this service source must also act as a scope, create the scope and use its service provider if (_serviceScope != RootScopeQualifiedName) { // create the scoped provider var serviceScope = ScopeTypesAndNames.GetType(_serviceScope); var scopedProvider = serviceProvider .GetRequiredService() .GetScopedProvider(serviceScope); // convert it also to an IServiceProvider var serviceProviderCreator = serviceProvider.GetRequiredService>(); serviceProvider = serviceProviderCreator(scopedProvider); // invoke onScopeCreated, if present // TODO: replace with a source code generation by turning this method private partial with Action argument, and every invocation site calls the necessary provider.Get<...> to build the argument list if (onScopeCreated != null) { var parameters = onScopeCreated.Method.GetParameters() .Select(parameterInfo => scopedProvider.Get(parameterInfo.ParameterType)); onScopeCreated.DynamicInvoke(parameters); } } else if (onScopeCreated != null) { logger.LogWarning("onScopeCreated passed during instantiation, but no scoped provider was created."); } // resolve the injected nodes against the chosen service provider // TODO: move to source generator, make it the only way for injection foreach (var node in InjectedNodes) { if (node == null) continue; logger.LogTrace("Injecting {Node}.", node.Name); var nodeType = node.GetType(); foreach (var field in nodeType .GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)) { if (field.GetCustomAttribute() == null) continue; var fieldType = field.FieldType; var service = serviceProvider.GetRequiredService(fieldType); field.SetValue(node, service); } foreach (var method in nodeType.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)) { if (method.GetCustomAttribute() == null) continue; var parameters = method.GetParameters(); var values = new object?[parameters.Length]; var i = 0; foreach (var parameter in parameters) { var parameterType = parameter.ParameterType; var service = serviceProvider.GetRequiredService(parameterType); values[i++] = service; } method.Invoke(node, values); } } } }