mirror of
https://github.com/goatcorp/Dalamud.git
synced 2026-01-03 14:23:40 +01:00
Merge pull request #1966 from Soreepeong/fix/servicescope
ServiceScope fixes
This commit is contained in:
commit
8c593bc31d
5 changed files with 165 additions and 166 deletions
|
|
@ -84,72 +84,57 @@ internal class ServiceContainer : IServiceProvider, IServiceType
|
||||||
/// <param name="scopedObjects">Scoped objects to be included in the constructor.</param>
|
/// <param name="scopedObjects">Scoped objects to be included in the constructor.</param>
|
||||||
/// <param name="scope">The scope to be used to create scoped services.</param>
|
/// <param name="scope">The scope to be used to create scoped services.</param>
|
||||||
/// <returns>The created object.</returns>
|
/// <returns>The created object.</returns>
|
||||||
public async Task<object?> CreateAsync(Type objectType, object[] scopedObjects, IServiceScope? scope = null)
|
public async Task<object> CreateAsync(Type objectType, object[] scopedObjects, IServiceScope? scope = null)
|
||||||
{
|
{
|
||||||
var scopeImpl = scope as ServiceScopeImpl;
|
var errorStep = "constructor lookup";
|
||||||
|
|
||||||
var ctor = this.FindApplicableCtor(objectType, scopedObjects);
|
try
|
||||||
if (ctor == null)
|
|
||||||
{
|
{
|
||||||
Log.Error("Failed to create {TypeName}, an eligible ctor with satisfiable services could not be found", objectType.FullName!);
|
var scopeImpl = scope as ServiceScopeImpl;
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// validate dependency versions (if they exist)
|
var ctor = this.FindApplicableCtor(objectType, scopedObjects)
|
||||||
var parameterTypes = ctor.GetParameters().Select(p => p.ParameterType).ToList();
|
?? throw new InvalidOperationException("An eligible ctor with satisfiable services could not be found");
|
||||||
|
|
||||||
var resolvedParams =
|
errorStep = "requested service resolution";
|
||||||
await Task.WhenAll(
|
var resolvedParams =
|
||||||
parameterTypes
|
await Task.WhenAll(
|
||||||
.Select(async type =>
|
ctor.GetParameters()
|
||||||
|
.Select(p => p.ParameterType)
|
||||||
|
.Select(type => this.GetService(type, scopeImpl, scopedObjects)));
|
||||||
|
|
||||||
|
var instance = RuntimeHelpers.GetUninitializedObject(objectType);
|
||||||
|
|
||||||
|
errorStep = "property injection";
|
||||||
|
await this.InjectProperties(instance, scopedObjects, scope);
|
||||||
|
|
||||||
|
errorStep = "ctor invocation";
|
||||||
|
var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||||
|
var thr = new Thread(
|
||||||
|
() =>
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
var service = await this.GetService(type, scopeImpl, scopedObjects);
|
ctor.Invoke(instance, resolvedParams);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
tcs.SetException(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (service == null)
|
tcs.SetResult();
|
||||||
{
|
});
|
||||||
Log.Error("Requested ctor service type {TypeName} was not available (null)", type.FullName!);
|
|
||||||
}
|
|
||||||
|
|
||||||
return service;
|
thr.Start();
|
||||||
}));
|
await tcs.Task.ConfigureAwait(false);
|
||||||
|
thr.Join();
|
||||||
|
|
||||||
var hasNull = resolvedParams.Any(p => p == null);
|
return instance;
|
||||||
if (hasNull)
|
|
||||||
{
|
|
||||||
Log.Error("Failed to create {TypeName}, a requested service type could not be satisfied", objectType.FullName!);
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
catch (Exception e)
|
||||||
var instance = RuntimeHelpers.GetUninitializedObject(objectType);
|
|
||||||
|
|
||||||
if (!await this.InjectProperties(instance, scopedObjects, scope))
|
|
||||||
{
|
{
|
||||||
Log.Error("Failed to create {TypeName}, a requested property service type could not be satisfied", objectType.FullName!);
|
throw new AggregateException($"Failed to create {objectType.FullName ?? objectType.Name} ({errorStep})", e);
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
|
|
||||||
var thr = new Thread(
|
|
||||||
() =>
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
ctor.Invoke(instance, resolvedParams);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
tcs.SetException(e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
tcs.SetResult();
|
|
||||||
});
|
|
||||||
|
|
||||||
thr.Start();
|
|
||||||
await tcs.Task.ConfigureAwait(false);
|
|
||||||
thr.Join();
|
|
||||||
|
|
||||||
return instance;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -159,28 +144,21 @@ internal class ServiceContainer : IServiceProvider, IServiceType
|
||||||
/// <param name="instance">The object instance.</param>
|
/// <param name="instance">The object instance.</param>
|
||||||
/// <param name="publicScopes">Scoped objects to be injected.</param>
|
/// <param name="publicScopes">Scoped objects to be injected.</param>
|
||||||
/// <param name="scope">The scope to be used to create scoped services.</param>
|
/// <param name="scope">The scope to be used to create scoped services.</param>
|
||||||
/// <returns>Whether or not the injection was successful.</returns>
|
/// <returns>A <see cref="ValueTask"/> representing the operation.</returns>
|
||||||
public async Task<bool> InjectProperties(object instance, object[] publicScopes, IServiceScope? scope = null)
|
public async Task InjectProperties(object instance, object[] publicScopes, IServiceScope? scope = null)
|
||||||
{
|
{
|
||||||
var scopeImpl = scope as ServiceScopeImpl;
|
var scopeImpl = scope as ServiceScopeImpl;
|
||||||
var objectType = instance.GetType();
|
var objectType = instance.GetType();
|
||||||
|
|
||||||
var props = objectType.GetProperties(BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public |
|
var props =
|
||||||
BindingFlags.NonPublic).Where(x => x.GetCustomAttributes(typeof(PluginServiceAttribute)).Any()).ToArray();
|
objectType
|
||||||
|
.GetProperties(
|
||||||
|
BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
|
||||||
|
.Where(x => x.GetCustomAttributes(typeof(PluginServiceAttribute)).Any())
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
foreach (var prop in props)
|
foreach (var prop in props)
|
||||||
{
|
prop.SetValue(instance, await this.GetService(prop.PropertyType, scopeImpl, publicScopes));
|
||||||
var service = await this.GetService(prop.PropertyType, scopeImpl, publicScopes);
|
|
||||||
if (service == null)
|
|
||||||
{
|
|
||||||
Log.Error("Requested service type {TypeName} was not available (null)", prop.PropertyType.FullName!);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
prop.SetValue(instance, service);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -192,7 +170,7 @@ internal class ServiceContainer : IServiceProvider, IServiceType
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
object? IServiceProvider.GetService(Type serviceType) => this.GetSingletonService(serviceType);
|
object? IServiceProvider.GetService(Type serviceType) => this.GetSingletonService(serviceType);
|
||||||
|
|
||||||
private async Task<object?> GetService(Type serviceType, ServiceScopeImpl? scope, object[] scopedObjects)
|
private async Task<object> GetService(Type serviceType, ServiceScopeImpl? scope, object[] scopedObjects)
|
||||||
{
|
{
|
||||||
if (this.interfaceToTypeMap.TryGetValue(serviceType, out var implementingType))
|
if (this.interfaceToTypeMap.TryGetValue(serviceType, out var implementingType))
|
||||||
serviceType = implementingType;
|
serviceType = implementingType;
|
||||||
|
|
@ -201,8 +179,8 @@ internal class ServiceContainer : IServiceProvider, IServiceType
|
||||||
{
|
{
|
||||||
if (scope == null)
|
if (scope == null)
|
||||||
{
|
{
|
||||||
Log.Error("Failed to create {TypeName}, is scoped but no scope provided", serviceType.FullName!);
|
throw new InvalidOperationException(
|
||||||
return null;
|
$"Failed to create {serviceType.FullName ?? serviceType.Name}, is scoped but no scope provided");
|
||||||
}
|
}
|
||||||
|
|
||||||
return await scope.CreatePrivateScopedObject(serviceType, scopedObjects);
|
return await scope.CreatePrivateScopedObject(serviceType, scopedObjects);
|
||||||
|
|
@ -210,18 +188,12 @@ internal class ServiceContainer : IServiceProvider, IServiceType
|
||||||
|
|
||||||
var singletonService = await this.GetSingletonService(serviceType, false);
|
var singletonService = await this.GetSingletonService(serviceType, false);
|
||||||
if (singletonService != null)
|
if (singletonService != null)
|
||||||
{
|
|
||||||
return singletonService;
|
return singletonService;
|
||||||
}
|
|
||||||
|
|
||||||
// resolve dependency from scoped objects
|
// resolve dependency from scoped objects
|
||||||
var scoped = scopedObjects.FirstOrDefault(o => o.GetType().IsAssignableTo(serviceType));
|
return scopedObjects.FirstOrDefault(o => o.GetType().IsAssignableTo(serviceType))
|
||||||
if (scoped == default)
|
?? throw new InvalidOperationException(
|
||||||
{
|
$"Requested type {serviceType.FullName ?? serviceType.Name} could not be found from {nameof(scopedObjects)}");
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return scoped;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<object?> GetSingletonService(Type serviceType, bool tryGetInterface = true)
|
private async Task<object?> GetSingletonService(Type serviceType, bool tryGetInterface = true)
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,10 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
using Serilog;
|
||||||
|
|
||||||
namespace Dalamud.IoC.Internal;
|
namespace Dalamud.IoC.Internal;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -14,7 +17,7 @@ internal interface IServiceScope : IDisposable
|
||||||
/// but not directly to created objects.
|
/// but not directly to created objects.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="scopes">The scopes to add.</param>
|
/// <param name="scopes">The scopes to add.</param>
|
||||||
public void RegisterPrivateScopes(params object[] scopes);
|
void RegisterPrivateScopes(params object[] scopes);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create an object.
|
/// Create an object.
|
||||||
|
|
@ -22,7 +25,7 @@ internal interface IServiceScope : IDisposable
|
||||||
/// <param name="objectType">The type of object to create.</param>
|
/// <param name="objectType">The type of object to create.</param>
|
||||||
/// <param name="scopedObjects">Scoped objects to be included in the constructor.</param>
|
/// <param name="scopedObjects">Scoped objects to be included in the constructor.</param>
|
||||||
/// <returns>The created object.</returns>
|
/// <returns>The created object.</returns>
|
||||||
public Task<object?> CreateAsync(Type objectType, params object[] scopedObjects);
|
Task<object> CreateAsync(Type objectType, params object[] scopedObjects);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Inject <see cref="PluginInterfaceAttribute" /> interfaces into public or static properties on the provided object.
|
/// Inject <see cref="PluginInterfaceAttribute" /> interfaces into public or static properties on the provided object.
|
||||||
|
|
@ -30,8 +33,8 @@ internal interface IServiceScope : IDisposable
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="instance">The object instance.</param>
|
/// <param name="instance">The object instance.</param>
|
||||||
/// <param name="scopedObjects">Scoped objects to be injected.</param>
|
/// <param name="scopedObjects">Scoped objects to be injected.</param>
|
||||||
/// <returns>Whether or not the injection was successful.</returns>
|
/// <returns>A <see cref="ValueTask"/> representing the status of the operation.</returns>
|
||||||
public Task<bool> InjectPropertiesAsync(object instance, params object[] scopedObjects);
|
Task InjectPropertiesAsync(object instance, params object[] scopedObjects);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -41,35 +44,24 @@ internal class ServiceScopeImpl : IServiceScope
|
||||||
{
|
{
|
||||||
private readonly ServiceContainer container;
|
private readonly ServiceContainer container;
|
||||||
|
|
||||||
private readonly List<object> privateScopedObjects = new();
|
private readonly List<object> privateScopedObjects = [];
|
||||||
private readonly List<object> scopeCreatedObjects = new();
|
private readonly ConcurrentDictionary<Type, Task<object>> scopeCreatedObjects = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>Initializes a new instance of the <see cref="ServiceScopeImpl" /> class.</summary>
|
||||||
/// Initializes a new instance of the <see cref="ServiceScopeImpl" /> class.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="container">The container this scope will use to create services.</param>
|
/// <param name="container">The container this scope will use to create services.</param>
|
||||||
public ServiceScopeImpl(ServiceContainer container)
|
public ServiceScopeImpl(ServiceContainer container) => this.container = container;
|
||||||
{
|
|
||||||
this.container = container;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void RegisterPrivateScopes(params object[] scopes)
|
public void RegisterPrivateScopes(params object[] scopes) =>
|
||||||
{
|
|
||||||
this.privateScopedObjects.AddRange(scopes);
|
this.privateScopedObjects.AddRange(scopes);
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public Task<object?> CreateAsync(Type objectType, params object[] scopedObjects)
|
public Task<object> CreateAsync(Type objectType, params object[] scopedObjects) =>
|
||||||
{
|
this.container.CreateAsync(objectType, scopedObjects, this);
|
||||||
return this.container.CreateAsync(objectType, scopedObjects, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public Task<bool> InjectPropertiesAsync(object instance, params object[] scopedObjects)
|
public Task InjectPropertiesAsync(object instance, params object[] scopedObjects) =>
|
||||||
{
|
this.container.InjectProperties(instance, scopedObjects, this);
|
||||||
return this.container.InjectProperties(instance, scopedObjects, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a service scoped to this scope, with private scoped objects.
|
/// Create a service scoped to this scope, with private scoped objects.
|
||||||
|
|
@ -77,34 +69,39 @@ internal class ServiceScopeImpl : IServiceScope
|
||||||
/// <param name="objectType">The type of object to create.</param>
|
/// <param name="objectType">The type of object to create.</param>
|
||||||
/// <param name="scopedObjects">Additional scoped objects.</param>
|
/// <param name="scopedObjects">Additional scoped objects.</param>
|
||||||
/// <returns>The created object, or null.</returns>
|
/// <returns>The created object, or null.</returns>
|
||||||
public async Task<object?> CreatePrivateScopedObject(Type objectType, params object[] scopedObjects)
|
public Task<object> CreatePrivateScopedObject(Type objectType, params object[] scopedObjects) =>
|
||||||
{
|
this.scopeCreatedObjects.GetOrAdd(
|
||||||
var instance = this.scopeCreatedObjects.FirstOrDefault(x => x.GetType() == objectType);
|
objectType,
|
||||||
if (instance != null)
|
static (objectType, p) => p.Scope.container.CreateAsync(
|
||||||
return instance;
|
objectType,
|
||||||
|
p.Objects.Concat(p.Scope.privateScopedObjects).ToArray()),
|
||||||
instance =
|
(Scope: this, Objects: scopedObjects));
|
||||||
await this.container.CreateAsync(objectType, scopedObjects.Concat(this.privateScopedObjects).ToArray());
|
|
||||||
if (instance != null)
|
|
||||||
this.scopeCreatedObjects.Add(instance);
|
|
||||||
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
foreach (var createdObject in this.scopeCreatedObjects)
|
foreach (var objectTask in this.scopeCreatedObjects)
|
||||||
{
|
{
|
||||||
switch (createdObject)
|
objectTask.Value.ContinueWith(
|
||||||
{
|
static r =>
|
||||||
case IInternalDisposableService d:
|
{
|
||||||
d.DisposeService();
|
if (!r.IsCompletedSuccessfully)
|
||||||
break;
|
{
|
||||||
case IDisposable d:
|
if (r.Exception is { } e)
|
||||||
d.Dispose();
|
Log.Error(e, "{what}: Failed to load.", nameof(ServiceScopeImpl));
|
||||||
break;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch (r.Result)
|
||||||
|
{
|
||||||
|
case IInternalDisposableService d:
|
||||||
|
d.DisposeService();
|
||||||
|
break;
|
||||||
|
case IDisposable d:
|
||||||
|
d.Dispose();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
using Dalamud.Configuration;
|
using Dalamud.Configuration;
|
||||||
using Dalamud.Configuration.Internal;
|
using Dalamud.Configuration.Internal;
|
||||||
|
|
@ -26,6 +27,8 @@ using Dalamud.Plugin.Ipc;
|
||||||
using Dalamud.Plugin.Ipc.Exceptions;
|
using Dalamud.Plugin.Ipc.Exceptions;
|
||||||
using Dalamud.Plugin.Ipc.Internal;
|
using Dalamud.Plugin.Ipc.Internal;
|
||||||
|
|
||||||
|
using Serilog;
|
||||||
|
|
||||||
namespace Dalamud.Plugin;
|
namespace Dalamud.Plugin;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -458,34 +461,52 @@ internal sealed class DalamudPluginInterface : IDalamudPluginInterface, IDisposa
|
||||||
|
|
||||||
#region Dependency Injection
|
#region Dependency Injection
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc/>
|
||||||
/// Create a new object of the provided type using its default constructor, then inject objects and properties.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="scopedObjects">Objects to inject additionally.</param>
|
|
||||||
/// <typeparam name="T">The type to create.</typeparam>
|
|
||||||
/// <returns>The created and initialized type.</returns>
|
|
||||||
public T? Create<T>(params object[] scopedObjects) where T : class
|
public T? Create<T>(params object[] scopedObjects) where T : class
|
||||||
{
|
{
|
||||||
var svcContainer = Service<IoC.Internal.ServiceContainer>.Get();
|
var t = this.CreateAsync<T>(scopedObjects);
|
||||||
|
t.Wait();
|
||||||
|
|
||||||
return (T)this.plugin.ServiceScope!.CreateAsync(
|
if (t.Exception is { } e)
|
||||||
typeof(T),
|
{
|
||||||
this.GetPublicIocScopes(scopedObjects)).GetAwaiter().GetResult();
|
Log.Error(
|
||||||
|
e,
|
||||||
|
"{who}: Exception during {where}: {what}",
|
||||||
|
this.plugin.Name,
|
||||||
|
nameof(this.Create),
|
||||||
|
typeof(T).FullName ?? typeof(T).Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return t.IsCompletedSuccessfully ? t.Result : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc/>
|
||||||
/// Inject services into properties on the provided object instance.
|
public async Task<T> CreateAsync<T>(params object[] scopedObjects) where T : class =>
|
||||||
/// </summary>
|
(T)await this.plugin.ServiceScope!.CreateAsync(typeof(T), this.GetPublicIocScopes(scopedObjects));
|
||||||
/// <param name="instance">The instance to inject services into.</param>
|
|
||||||
/// <param name="scopedObjects">Objects to inject additionally.</param>
|
/// <inheritdoc/>
|
||||||
/// <returns>Whether or not the injection succeeded.</returns>
|
|
||||||
public bool Inject(object instance, params object[] scopedObjects)
|
public bool Inject(object instance, params object[] scopedObjects)
|
||||||
{
|
{
|
||||||
return this.plugin.ServiceScope!.InjectPropertiesAsync(
|
var t = this.InjectAsync(instance, scopedObjects);
|
||||||
instance,
|
t.Wait();
|
||||||
this.GetPublicIocScopes(scopedObjects)).GetAwaiter().GetResult();
|
|
||||||
|
if (t.Exception is { } e)
|
||||||
|
{
|
||||||
|
Log.Error(
|
||||||
|
e,
|
||||||
|
"{who}: Exception during {where}: {what}",
|
||||||
|
this.plugin.Name,
|
||||||
|
nameof(this.Inject),
|
||||||
|
instance.GetType().FullName ?? instance.GetType().Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return t.IsCompletedSuccessfully;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public Task InjectAsync(object instance, params object[] scopedObjects) =>
|
||||||
|
this.plugin.ServiceScope!.InjectPropertiesAsync(instance, this.GetPublicIocScopes(scopedObjects));
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
/// <summary>Unregister the plugin and dispose all references.</summary>
|
/// <summary>Unregister the plugin and dispose all references.</summary>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
using Dalamud.Configuration;
|
using Dalamud.Configuration;
|
||||||
using Dalamud.Game.Text;
|
using Dalamud.Game.Text;
|
||||||
|
|
@ -304,14 +305,30 @@ public interface IDalamudPluginInterface
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="scopedObjects">Objects to inject additionally.</param>
|
/// <param name="scopedObjects">Objects to inject additionally.</param>
|
||||||
/// <typeparam name="T">The type to create.</typeparam>
|
/// <typeparam name="T">The type to create.</typeparam>
|
||||||
/// <returns>The created and initialized type.</returns>
|
/// <returns>The created and initialized type, or <c>null</c> on failure.</returns>
|
||||||
T? Create<T>(params object[] scopedObjects) where T : class;
|
T? Create<T>(params object[] scopedObjects) where T : class;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a new object of the provided type using its default constructor, then inject objects and properties.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="scopedObjects">Objects to inject additionally.</param>
|
||||||
|
/// <typeparam name="T">The type to create.</typeparam>
|
||||||
|
/// <returns>A task representing the created and initialized type.</returns>
|
||||||
|
Task<T> CreateAsync<T>(params object[] scopedObjects) where T : class;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Inject services into properties on the provided object instance.
|
/// Inject services into properties on the provided object instance.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="instance">The instance to inject services into.</param>
|
/// <param name="instance">The instance to inject services into.</param>
|
||||||
/// <param name="scopedObjects">Objects to inject additionally.</param>
|
/// <param name="scopedObjects">Objects to inject additionally.</param>
|
||||||
/// <returns>Whether or not the injection succeeded.</returns>
|
/// <returns>Whether the injection succeeded.</returns>
|
||||||
bool Inject(object instance, params object[] scopedObjects);
|
bool Inject(object instance, params object[] scopedObjects);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Inject services into properties on the provided object instance.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="instance">The instance to inject services into.</param>
|
||||||
|
/// <param name="scopedObjects">Objects to inject additionally.</param>
|
||||||
|
/// <returns>A <see cref="ValueTask"/> representing the status of the operation.</returns>
|
||||||
|
Task InjectAsync(object instance, params object[] scopedObjects);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -417,24 +417,16 @@ internal class LocalPlugin : IDisposable
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (this.manifest.LoadSync && this.manifest.LoadRequiredState is 0 or 1)
|
var forceFrameworkThread = this.manifest.LoadSync && this.manifest.LoadRequiredState is 0 or 1;
|
||||||
{
|
var newInstanceTask = forceFrameworkThread ? framework.RunOnFrameworkThread(Create) : Create();
|
||||||
var newInstance = await framework.RunOnFrameworkThread(
|
this.instance = await newInstanceTask.ConfigureAwait(false);
|
||||||
() => this.ServiceScope.CreateAsync(
|
|
||||||
this.pluginType!,
|
async Task<IDalamudPlugin> Create() =>
|
||||||
this.DalamudInterface!)).ConfigureAwait(false);
|
(IDalamudPlugin)await this.ServiceScope!.CreateAsync(this.pluginType!, this.DalamudInterface!);
|
||||||
|
|
||||||
this.instance = newInstance as IDalamudPlugin;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
this.instance =
|
|
||||||
await this.ServiceScope.CreateAsync(this.pluginType!, this.DalamudInterface!) as IDalamudPlugin;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Log.Error(ex, "Exception in plugin constructor");
|
Log.Error(ex, "Exception during plugin initialization");
|
||||||
this.instance = null;
|
this.instance = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue