mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-12 18:27:23 +01:00
feat: inject properties before calling ctor
This commit is contained in:
parent
bed7973a95
commit
48081bba7d
3 changed files with 110 additions and 26 deletions
|
|
@ -2,6 +2,7 @@ using System;
|
||||||
|
|
||||||
using Dalamud.Game.Command;
|
using Dalamud.Game.Command;
|
||||||
using Dalamud.Interface.Windowing;
|
using Dalamud.Interface.Windowing;
|
||||||
|
using Dalamud.IoC;
|
||||||
using Dalamud.Logging;
|
using Dalamud.Logging;
|
||||||
using Dalamud.Plugin;
|
using Dalamud.Plugin;
|
||||||
|
|
||||||
|
|
@ -17,6 +18,9 @@ namespace Dalamud.CorePlugin
|
||||||
|
|
||||||
// private Localization localizationManager;
|
// private Localization localizationManager;
|
||||||
|
|
||||||
|
[PluginService]
|
||||||
|
public static CommandManager CmdManager { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="PluginImpl"/> class.
|
/// Initializes a new instance of the <see cref="PluginImpl"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -33,7 +37,9 @@ namespace Dalamud.CorePlugin
|
||||||
this.Interface.UiBuilder.Draw += this.OnDraw;
|
this.Interface.UiBuilder.Draw += this.OnDraw;
|
||||||
this.Interface.UiBuilder.OpenConfigUi += this.OnOpenConfigUi;
|
this.Interface.UiBuilder.OpenConfigUi += this.OnOpenConfigUi;
|
||||||
|
|
||||||
Service<CommandManager>.Get().AddHandler("/di", new(this.OnCommand) { HelpMessage = $"Access the {this.Name} plugin." });
|
CmdManager.AddHandler("/coreplug", new(this.OnCommand) { HelpMessage = $"Access the {this.Name} plugin." });
|
||||||
|
|
||||||
|
PluginLog.Information("CorePlugin ctor!");
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|
@ -52,7 +58,7 @@ namespace Dalamud.CorePlugin
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
Service<CommandManager>.Get().RemoveHandler("/di");
|
CmdManager.RemoveHandler("/coreplug");
|
||||||
|
|
||||||
this.Interface.UiBuilder.Draw -= this.OnDraw;
|
this.Interface.UiBuilder.Draw -= this.OnDraw;
|
||||||
|
|
||||||
|
|
@ -92,6 +98,7 @@ namespace Dalamud.CorePlugin
|
||||||
|
|
||||||
private void OnCommand(string command, string args)
|
private void OnCommand(string command, string args)
|
||||||
{
|
{
|
||||||
|
PluginLog.Information("Command called!");
|
||||||
// this.window.IsOpen = true;
|
// this.window.IsOpen = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,9 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using System.Runtime.Serialization;
|
||||||
|
|
||||||
using Serilog;
|
using Dalamud.Logging.Internal;
|
||||||
|
|
||||||
namespace Dalamud.IoC.Internal
|
namespace Dalamud.IoC.Internal
|
||||||
{
|
{
|
||||||
|
|
@ -12,6 +13,8 @@ namespace Dalamud.IoC.Internal
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal class ServiceContainer : IServiceProvider
|
internal class ServiceContainer : IServiceProvider
|
||||||
{
|
{
|
||||||
|
private static readonly ModuleLog Log = new("SERVICECONTAINER");
|
||||||
|
|
||||||
private readonly Dictionary<Type, ObjectInstance> instances = new();
|
private readonly Dictionary<Type, ObjectInstance> instances = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -52,28 +55,7 @@ namespace Dalamud.IoC.Internal
|
||||||
return (parameterType, requiredVersion);
|
return (parameterType, requiredVersion);
|
||||||
});
|
});
|
||||||
|
|
||||||
var versionCheck = parameters.All(p =>
|
var versionCheck = parameters.All(p => CheckInterfaceVersion(p.requiredVersion, p.parameterType));
|
||||||
{
|
|
||||||
// if there's no required version, ignore it
|
|
||||||
if (p.requiredVersion == null)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// if there's no requested version, ignore it
|
|
||||||
var declVersion = p.parameterType.GetCustomAttribute<InterfaceVersionAttribute>();
|
|
||||||
if (declVersion == null)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (declVersion.Version == p.requiredVersion.Version)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
Log.Error(
|
|
||||||
"Requested version {ReqVersion} does not match the implemented version {ImplVersion} for param type {ParamType}",
|
|
||||||
p.requiredVersion.Version,
|
|
||||||
declVersion.Version,
|
|
||||||
p.parameterType.FullName);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!versionCheck)
|
if (!versionCheck)
|
||||||
{
|
{
|
||||||
|
|
@ -102,12 +84,95 @@ namespace Dalamud.IoC.Internal
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Activator.CreateInstance(objectType, resolvedParams);
|
var instance = FormatterServices.GetUninitializedObject(objectType);
|
||||||
|
|
||||||
|
if (!this.InjectProperties(instance, scopedObjects))
|
||||||
|
{
|
||||||
|
Log.Error("Failed to create {TypeName}, a requested property service type could not be satisfied", objectType.FullName);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctor.Invoke(instance, scopedObjects);
|
||||||
|
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Inject <see cref="PluginInterfaceAttribute"/> interfaces into public or static properties on the provided object.
|
||||||
|
/// The properties have to be marked with the <see cref="PluginServiceAttribute"/>.
|
||||||
|
/// The properties can be marked with the <see cref="RequiredVersionAttribute"/> to lock down versions.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="instance">The object instance.</param>
|
||||||
|
/// <param name="scopedObjects">Scoped objects.</param>
|
||||||
|
/// <returns>Whether or not the injection was successful.</returns>
|
||||||
|
public bool InjectProperties(object instance, params object[] scopedObjects)
|
||||||
|
{
|
||||||
|
var objectType = instance.GetType();
|
||||||
|
|
||||||
|
Log.Information($"Injecting props into {objectType.FullName}");
|
||||||
|
|
||||||
|
var props = objectType.GetProperties(BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public |
|
||||||
|
BindingFlags.NonPublic).Where(x => x.GetCustomAttributes(typeof(PluginServiceAttribute)).Any()).Select(
|
||||||
|
propertyInfo =>
|
||||||
|
{
|
||||||
|
var requiredVersion = propertyInfo.GetCustomAttribute(typeof(RequiredVersionAttribute)) as RequiredVersionAttribute;
|
||||||
|
return (propertyInfo, requiredVersion);
|
||||||
|
}).ToArray();
|
||||||
|
|
||||||
|
var versionCheck = props.All(x => CheckInterfaceVersion(x.requiredVersion, x.propertyInfo.PropertyType));
|
||||||
|
|
||||||
|
if (!versionCheck)
|
||||||
|
{
|
||||||
|
Log.Error("Failed to create {TypeName}, a RequestedVersion could not be satisfied", objectType.FullName);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var prop in props)
|
||||||
|
{
|
||||||
|
Log.Information($"Injecting {prop.propertyInfo.Name} for type {prop.propertyInfo.PropertyType.GetType().FullName}");
|
||||||
|
|
||||||
|
var service = this.GetService(prop.propertyInfo.PropertyType, scopedObjects);
|
||||||
|
|
||||||
|
if (service == null)
|
||||||
|
{
|
||||||
|
Log.Error("Requested service type {TypeName} was not available (null)", prop.propertyInfo.PropertyType.FullName);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
prop.propertyInfo.SetValue(instance, service);
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.Information("Injected");
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
object? IServiceProvider.GetService(Type serviceType) => this.GetService(serviceType);
|
object? IServiceProvider.GetService(Type serviceType) => this.GetService(serviceType);
|
||||||
|
|
||||||
|
private static bool CheckInterfaceVersion(RequiredVersionAttribute? requiredVersion, Type parameterType)
|
||||||
|
{
|
||||||
|
// if there's no required version, ignore it
|
||||||
|
if (requiredVersion == null)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// if there's no requested version, ignore it
|
||||||
|
var declVersion = parameterType.GetCustomAttribute<InterfaceVersionAttribute>();
|
||||||
|
if (declVersion == null)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (declVersion.Version == requiredVersion.Version)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
Log.Error(
|
||||||
|
"Requested version {ReqVersion} does not match the implemented version {ImplVersion} for param type {ParamType}",
|
||||||
|
requiredVersion.Version,
|
||||||
|
declVersion.Version,
|
||||||
|
parameterType.FullName);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private object? GetService(Type serviceType, object[] scopedObjects)
|
private object? GetService(Type serviceType, object[] scopedObjects)
|
||||||
{
|
{
|
||||||
var singletonService = this.GetService(serviceType);
|
var singletonService = this.GetService(serviceType);
|
||||||
|
|
|
||||||
12
Dalamud/IoC/PluginServiceAttribute.cs
Normal file
12
Dalamud/IoC/PluginServiceAttribute.cs
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Dalamud.IoC
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// This attribute indicates whether an applicable service should be injected into the plugin.
|
||||||
|
/// </summary>
|
||||||
|
[AttributeUsage(AttributeTargets.Property)]
|
||||||
|
public class PluginServiceAttribute : Attribute
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue