Add call-on-services-ready attribute for service methods, and fix scene nullability (#900)

This commit is contained in:
kizer 2022-06-27 01:49:34 +09:00 committed by GitHub
parent 5809accf5d
commit 3369f569fb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
52 changed files with 274 additions and 273 deletions

View file

@ -6,7 +6,6 @@ using System.Reflection;
using System.Threading.Tasks;
using Dalamud.Configuration.Internal;
using Dalamud.Game;
using Dalamud.Interface.Internal;
using Dalamud.IoC.Internal;
using Dalamud.Logging.Internal;
using Dalamud.Utility.Timing;
@ -67,7 +66,6 @@ namespace Dalamud
var earlyLoadingServices = new HashSet<Type>();
var blockingEarlyLoadingServices = new HashSet<Type>();
var afterDrawingEarlyLoadedServices = new HashSet<Type>();
var dependencyServicesMap = new Dictionary<Type, List<Type>>();
var getAsyncTaskMap = new Dictionary<Type, Task>();
@ -90,10 +88,6 @@ namespace Dalamud
getAsyncTaskMap[serviceType] = getTask;
blockingEarlyLoadingServices.Add(serviceType);
}
else if (attr.IsAssignableTo(typeof(AfterDrawingEarlyLoadedService)))
{
afterDrawingEarlyLoadedServices.Add(serviceType);
}
else
{
earlyLoadingServices.Add(serviceType);
@ -126,55 +120,44 @@ namespace Dalamud
try
{
for (var i = 0; i < 2; i++)
var tasks = new List<Task>();
var servicesToLoad = new HashSet<Type>();
servicesToLoad.UnionWith(earlyLoadingServices);
servicesToLoad.UnionWith(blockingEarlyLoadingServices);
while (servicesToLoad.Any())
{
var tasks = new List<Task>();
var servicesToLoad = new HashSet<Type>();
if (i == 0)
foreach (var serviceType in servicesToLoad)
{
servicesToLoad.UnionWith(earlyLoadingServices);
servicesToLoad.UnionWith(blockingEarlyLoadingServices);
if (dependencyServicesMap[serviceType].Any(
x => getAsyncTaskMap.GetValueOrDefault(x)?.IsCompleted == false))
continue;
tasks.Add((Task)service.MakeGenericType(serviceType).InvokeMember(
"StartLoader",
BindingFlags.InvokeMethod | BindingFlags.Static | BindingFlags.Public,
null,
null,
null));
servicesToLoad.Remove(serviceType);
}
if (!tasks.Any())
throw new InvalidOperationException("Unresolvable dependency cycle detected");
if (servicesToLoad.Any())
{
await Task.WhenAny(tasks);
var faultedTasks = tasks.Where(x => x.IsFaulted).Select(x => (Exception)x.Exception!).ToArray();
if (faultedTasks.Any())
throw new AggregateException(faultedTasks);
}
else
{
servicesToLoad.UnionWith(afterDrawingEarlyLoadedServices);
await (await Service<InterfaceManager>.GetAsync()).SceneInitializeTask;
await Task.WhenAll(tasks);
}
while (servicesToLoad.Any())
{
foreach (var serviceType in servicesToLoad)
{
if (dependencyServicesMap[serviceType].Any(
x => getAsyncTaskMap.GetValueOrDefault(x)?.IsCompleted == false))
continue;
tasks.Add((Task)service.MakeGenericType(serviceType).InvokeMember(
"StartLoader",
BindingFlags.InvokeMethod | BindingFlags.Static | BindingFlags.Public,
null,
null,
null));
servicesToLoad.Remove(serviceType);
}
if (!tasks.Any())
throw new InvalidOperationException("Unresolvable dependency cycle detected");
if (servicesToLoad.Any())
{
await Task.WhenAny(tasks);
var faultedTasks = tasks.Where(x => x.IsFaulted).Select(x => (Exception)x.Exception!).ToArray();
if (faultedTasks.Any())
throw new AggregateException(faultedTasks);
}
else
{
await Task.WhenAll(tasks);
}
tasks.RemoveAll(x => x.IsCompleted);
}
tasks.RemoveAll(x => x.IsCompleted);
}
}
catch (Exception e)
@ -236,11 +219,11 @@ namespace Dalamud
}
/// <summary>
/// Indicates that the class is a service, and will be instantiated automatically on startup,
/// when drawing becomes available.
/// Indicates that the method should be called when the services given in the constructor are ready.
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
public class AfterDrawingEarlyLoadedService : EarlyLoadedService
[AttributeUsage(AttributeTargets.Method)]
[MeansImplicitUse]
public class CallWhenServicesReady : Attribute
{
}
}