You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

89 lines
3.6 KiB

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Reflection;
  5. using IPA.Config;
  6. using IPA.Logging;
  7. using IPA.Utilities;
  8. namespace IPA.Loader
  9. {
  10. /// <summary>
  11. /// The type that handles value injecting into a plugin's Init.
  12. /// </summary>
  13. public static class PluginInitInjector
  14. {
  15. /// <summary>
  16. /// A typed injector for a plugin's Init method. When registered, called for all associated types. If it returns null, the default for the type will be used.
  17. /// </summary>
  18. /// <param name="previous">the previous return value of the function, or <see langword="null"/> if never called for plugin.</param>
  19. /// <param name="param">the <see cref="ParameterInfo"/> of the parameter being injected.</param>
  20. /// <param name="meta">the <see cref="PluginLoader.PluginMetadata"/> for the plugin being loaded.</param>
  21. /// <returns>the value to inject into that parameter.</returns>
  22. public delegate object InjectParameter(object previous, ParameterInfo param, PluginLoader.PluginMetadata meta);
  23. /// <summary>
  24. /// Adds an injector to be used when calling future plugins' Init methods.
  25. /// </summary>
  26. /// <param name="type">the type of the parameter.</param>
  27. /// <param name="injector">the function to call for injection.</param>
  28. public static void AddInjector(Type type, InjectParameter injector)
  29. {
  30. injectors.Add(new Tuple<Type, InjectParameter>(type, injector));
  31. }
  32. private static readonly List<Tuple<Type, InjectParameter>> injectors = new List<Tuple<Type, InjectParameter>>
  33. {
  34. new Tuple<Type, InjectParameter>(typeof(Logger), (prev, param, meta) => prev ?? new StandardLogger(meta.Name)),
  35. new Tuple<Type, InjectParameter>(typeof(IModPrefs), (prev, param, meta) => prev ?? new ModPrefs(meta)),
  36. new Tuple<Type, InjectParameter>(typeof(IConfigProvider), (prev, param, meta) =>
  37. {
  38. if (prev != null) return prev;
  39. var cfgProvider = Config.Config.GetProviderFor(meta.Name, param);
  40. cfgProvider.Load();
  41. return cfgProvider;
  42. })
  43. };
  44. internal static void Inject(MethodInfo init, PluginLoader.PluginInfo info)
  45. {
  46. var instance = info.Plugin;
  47. var meta = info.Metadata;
  48. var initArgs = new List<object>();
  49. var initParams = init.GetParameters();
  50. Dictionary<Tuple<Type, InjectParameter>, object> previousValues =
  51. new Dictionary<Tuple<Type, InjectParameter>, object>(injectors.Count);
  52. foreach (var param in initParams)
  53. {
  54. var paramType = param.ParameterType;
  55. var value = paramType.GetDefault();
  56. foreach (var pair in injectors.Where(t => paramType.IsAssignableFrom(t.Item1)))
  57. {
  58. object prev = null;
  59. if (previousValues.ContainsKey(pair))
  60. prev = previousValues[pair];
  61. var val = pair.Item2?.Invoke(prev, param, meta);
  62. if (previousValues.ContainsKey(pair))
  63. previousValues[pair] = val;
  64. else
  65. previousValues.Add(pair, val);
  66. if (val == null) continue;
  67. value = val;
  68. break;
  69. }
  70. initArgs.Add(value);
  71. }
  72. init.Invoke(instance, initArgs.ToArray());
  73. }
  74. }
  75. }