mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-14 20:54:16 +01:00
chore: convert Dalamud to file-scoped namespaces
This commit is contained in:
parent
b093323acc
commit
987ff8dc8f
368 changed files with 55081 additions and 55450 deletions
|
|
@ -12,289 +12,288 @@ using Dalamud.Logging.Internal;
|
|||
using Dalamud.Utility.Timing;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace Dalamud
|
||||
namespace Dalamud;
|
||||
|
||||
/// <summary>
|
||||
/// Class to initialize Service<T>s.
|
||||
/// </summary>
|
||||
internal static class ServiceManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Class to initialize Service<T>s.
|
||||
/// Static log facility for Service{T}, to avoid duplicate instances for different types.
|
||||
/// </summary>
|
||||
internal static class ServiceManager
|
||||
public static readonly ModuleLog Log = new("SVC");
|
||||
|
||||
private static readonly TaskCompletionSource BlockingServicesLoadedTaskCompletionSource = new();
|
||||
|
||||
private static readonly List<Type> LoadedServices = new();
|
||||
|
||||
/// <summary>
|
||||
/// Gets task that gets completed when all blocking early loading services are done loading.
|
||||
/// </summary>
|
||||
public static Task BlockingResolved { get; } = BlockingServicesLoadedTaskCompletionSource.Task;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes Provided Services and FFXIVClientStructs.
|
||||
/// </summary>
|
||||
/// <param name="dalamud">Instance of <see cref="Dalamud"/>.</param>
|
||||
/// <param name="startInfo">Instance of <see cref="DalamudStartInfo"/>.</param>
|
||||
/// <param name="configuration">Instance of <see cref="DalamudConfiguration"/>.</param>
|
||||
public static void InitializeProvidedServicesAndClientStructs(Dalamud dalamud, DalamudStartInfo startInfo, DalamudConfiguration configuration)
|
||||
{
|
||||
/// <summary>
|
||||
/// Static log facility for Service{T}, to avoid duplicate instances for different types.
|
||||
/// </summary>
|
||||
public static readonly ModuleLog Log = new("SVC");
|
||||
// Initialize the process information.
|
||||
var cacheDir = new DirectoryInfo(Path.Combine(startInfo.WorkingDirectory!, "cachedSigs"));
|
||||
if (!cacheDir.Exists)
|
||||
cacheDir.Create();
|
||||
|
||||
private static readonly TaskCompletionSource BlockingServicesLoadedTaskCompletionSource = new();
|
||||
|
||||
private static readonly List<Type> LoadedServices = new();
|
||||
|
||||
/// <summary>
|
||||
/// Gets task that gets completed when all blocking early loading services are done loading.
|
||||
/// </summary>
|
||||
public static Task BlockingResolved { get; } = BlockingServicesLoadedTaskCompletionSource.Task;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes Provided Services and FFXIVClientStructs.
|
||||
/// </summary>
|
||||
/// <param name="dalamud">Instance of <see cref="Dalamud"/>.</param>
|
||||
/// <param name="startInfo">Instance of <see cref="DalamudStartInfo"/>.</param>
|
||||
/// <param name="configuration">Instance of <see cref="DalamudConfiguration"/>.</param>
|
||||
public static void InitializeProvidedServicesAndClientStructs(Dalamud dalamud, DalamudStartInfo startInfo, DalamudConfiguration configuration)
|
||||
lock (LoadedServices)
|
||||
{
|
||||
// Initialize the process information.
|
||||
var cacheDir = new DirectoryInfo(Path.Combine(startInfo.WorkingDirectory!, "cachedSigs"));
|
||||
if (!cacheDir.Exists)
|
||||
cacheDir.Create();
|
||||
Service<Dalamud>.Provide(dalamud);
|
||||
LoadedServices.Add(typeof(Dalamud));
|
||||
|
||||
lock (LoadedServices)
|
||||
{
|
||||
Service<Dalamud>.Provide(dalamud);
|
||||
LoadedServices.Add(typeof(Dalamud));
|
||||
Service<DalamudStartInfo>.Provide(startInfo);
|
||||
LoadedServices.Add(typeof(DalamudStartInfo));
|
||||
|
||||
Service<DalamudStartInfo>.Provide(startInfo);
|
||||
LoadedServices.Add(typeof(DalamudStartInfo));
|
||||
Service<DalamudConfiguration>.Provide(configuration);
|
||||
LoadedServices.Add(typeof(DalamudConfiguration));
|
||||
|
||||
Service<DalamudConfiguration>.Provide(configuration);
|
||||
LoadedServices.Add(typeof(DalamudConfiguration));
|
||||
Service<ServiceContainer>.Provide(new ServiceContainer());
|
||||
LoadedServices.Add(typeof(ServiceContainer));
|
||||
|
||||
Service<ServiceContainer>.Provide(new ServiceContainer());
|
||||
LoadedServices.Add(typeof(ServiceContainer));
|
||||
|
||||
Service<SigScanner>.Provide(
|
||||
new SigScanner(
|
||||
true, new FileInfo(Path.Combine(cacheDir.FullName, $"{startInfo.GameVersion}.json"))));
|
||||
LoadedServices.Add(typeof(SigScanner));
|
||||
}
|
||||
|
||||
using (Timings.Start("CS Resolver Init"))
|
||||
{
|
||||
FFXIVClientStructs.Resolver.InitializeParallel(
|
||||
new FileInfo(Path.Combine(cacheDir.FullName, $"{startInfo.GameVersion}_cs.json")));
|
||||
}
|
||||
Service<SigScanner>.Provide(
|
||||
new SigScanner(
|
||||
true, new FileInfo(Path.Combine(cacheDir.FullName, $"{startInfo.GameVersion}.json"))));
|
||||
LoadedServices.Add(typeof(SigScanner));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Kicks off construction of services that can handle early loading.
|
||||
/// </summary>
|
||||
/// <returns>Task for initializing all services.</returns>
|
||||
public static async Task InitializeEarlyLoadableServices()
|
||||
using (Timings.Start("CS Resolver Init"))
|
||||
{
|
||||
using var serviceInitializeTimings = Timings.Start("Services Init");
|
||||
FFXIVClientStructs.Resolver.InitializeParallel(
|
||||
new FileInfo(Path.Combine(cacheDir.FullName, $"{startInfo.GameVersion}_cs.json")));
|
||||
}
|
||||
}
|
||||
|
||||
var earlyLoadingServices = new HashSet<Type>();
|
||||
var blockingEarlyLoadingServices = new HashSet<Type>();
|
||||
/// <summary>
|
||||
/// Kicks off construction of services that can handle early loading.
|
||||
/// </summary>
|
||||
/// <returns>Task for initializing all services.</returns>
|
||||
public static async Task InitializeEarlyLoadableServices()
|
||||
{
|
||||
using var serviceInitializeTimings = Timings.Start("Services Init");
|
||||
|
||||
var dependencyServicesMap = new Dictionary<Type, List<Type>>();
|
||||
var getAsyncTaskMap = new Dictionary<Type, Task>();
|
||||
var earlyLoadingServices = new HashSet<Type>();
|
||||
var blockingEarlyLoadingServices = new HashSet<Type>();
|
||||
|
||||
foreach (var serviceType in Assembly.GetExecutingAssembly().GetTypes())
|
||||
{
|
||||
var attr = serviceType.GetCustomAttribute<Service>(true)?.GetType();
|
||||
if (attr?.IsAssignableTo(typeof(EarlyLoadedService)) != true)
|
||||
continue;
|
||||
var dependencyServicesMap = new Dictionary<Type, List<Type>>();
|
||||
var getAsyncTaskMap = new Dictionary<Type, Task>();
|
||||
|
||||
var getTask = (Task)typeof(Service<>)
|
||||
.MakeGenericType(serviceType)
|
||||
.InvokeMember(
|
||||
"GetAsync",
|
||||
BindingFlags.InvokeMethod | BindingFlags.Static | BindingFlags.Public,
|
||||
null,
|
||||
null,
|
||||
null);
|
||||
foreach (var serviceType in Assembly.GetExecutingAssembly().GetTypes())
|
||||
{
|
||||
var attr = serviceType.GetCustomAttribute<Service>(true)?.GetType();
|
||||
if (attr?.IsAssignableTo(typeof(EarlyLoadedService)) != true)
|
||||
continue;
|
||||
|
||||
if (attr.IsAssignableTo(typeof(BlockingEarlyLoadedService)))
|
||||
{
|
||||
getAsyncTaskMap[serviceType] = getTask;
|
||||
blockingEarlyLoadingServices.Add(serviceType);
|
||||
}
|
||||
else
|
||||
{
|
||||
earlyLoadingServices.Add(serviceType);
|
||||
}
|
||||
|
||||
dependencyServicesMap[serviceType] =
|
||||
(List<Type>)typeof(Service<>)
|
||||
var getTask = (Task)typeof(Service<>)
|
||||
.MakeGenericType(serviceType)
|
||||
.InvokeMember(
|
||||
"GetDependencyServices",
|
||||
"GetAsync",
|
||||
BindingFlags.InvokeMethod | BindingFlags.Static | BindingFlags.Public,
|
||||
null,
|
||||
null,
|
||||
null);
|
||||
|
||||
if (attr.IsAssignableTo(typeof(BlockingEarlyLoadedService)))
|
||||
{
|
||||
getAsyncTaskMap[serviceType] = getTask;
|
||||
blockingEarlyLoadingServices.Add(serviceType);
|
||||
}
|
||||
else
|
||||
{
|
||||
earlyLoadingServices.Add(serviceType);
|
||||
}
|
||||
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
await Task.WhenAll(blockingEarlyLoadingServices.Select(x => getAsyncTaskMap[x]));
|
||||
BlockingServicesLoadedTaskCompletionSource.SetResult();
|
||||
Timings.Event("BlockingServices Initialized");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
BlockingServicesLoadedTaskCompletionSource.SetException(e);
|
||||
}
|
||||
}).ConfigureAwait(false);
|
||||
dependencyServicesMap[serviceType] =
|
||||
(List<Type>)typeof(Service<>)
|
||||
.MakeGenericType(serviceType)
|
||||
.InvokeMember(
|
||||
"GetDependencyServices",
|
||||
BindingFlags.InvokeMethod | BindingFlags.Static | BindingFlags.Public,
|
||||
null,
|
||||
null,
|
||||
null);
|
||||
}
|
||||
|
||||
var tasks = new List<Task>();
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var servicesToLoad = new HashSet<Type>();
|
||||
servicesToLoad.UnionWith(earlyLoadingServices);
|
||||
servicesToLoad.UnionWith(blockingEarlyLoadingServices);
|
||||
|
||||
while (servicesToLoad.Any())
|
||||
{
|
||||
foreach (var serviceType in servicesToLoad)
|
||||
{
|
||||
if (dependencyServicesMap[serviceType].Any(
|
||||
x => getAsyncTaskMap.GetValueOrDefault(x)?.IsCompleted == false))
|
||||
continue;
|
||||
|
||||
tasks.Add((Task)typeof(Service<>)
|
||||
.MakeGenericType(serviceType)
|
||||
.InvokeMember(
|
||||
"StartLoader",
|
||||
BindingFlags.InvokeMethod | BindingFlags.Static | BindingFlags.NonPublic,
|
||||
null,
|
||||
null,
|
||||
null));
|
||||
servicesToLoad.Remove(serviceType);
|
||||
|
||||
tasks.Add(tasks.Last().ContinueWith(task =>
|
||||
{
|
||||
if (task.IsFaulted)
|
||||
return;
|
||||
lock (LoadedServices)
|
||||
{
|
||||
LoadedServices.Add(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);
|
||||
}
|
||||
await Task.WhenAll(blockingEarlyLoadingServices.Select(x => getAsyncTaskMap[x]));
|
||||
BlockingServicesLoadedTaskCompletionSource.SetResult();
|
||||
Timings.Event("BlockingServices Initialized");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e, "Failed resolving services");
|
||||
try
|
||||
BlockingServicesLoadedTaskCompletionSource.SetException(e);
|
||||
}
|
||||
}).ConfigureAwait(false);
|
||||
|
||||
var tasks = new List<Task>();
|
||||
try
|
||||
{
|
||||
var servicesToLoad = new HashSet<Type>();
|
||||
servicesToLoad.UnionWith(earlyLoadingServices);
|
||||
servicesToLoad.UnionWith(blockingEarlyLoadingServices);
|
||||
|
||||
while (servicesToLoad.Any())
|
||||
{
|
||||
foreach (var serviceType in servicesToLoad)
|
||||
{
|
||||
BlockingServicesLoadedTaskCompletionSource.SetException(e);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// don't care, as this means task result/exception has already been set
|
||||
if (dependencyServicesMap[serviceType].Any(
|
||||
x => getAsyncTaskMap.GetValueOrDefault(x)?.IsCompleted == false))
|
||||
continue;
|
||||
|
||||
tasks.Add((Task)typeof(Service<>)
|
||||
.MakeGenericType(serviceType)
|
||||
.InvokeMember(
|
||||
"StartLoader",
|
||||
BindingFlags.InvokeMethod | BindingFlags.Static | BindingFlags.NonPublic,
|
||||
null,
|
||||
null,
|
||||
null));
|
||||
servicesToLoad.Remove(serviceType);
|
||||
|
||||
tasks.Add(tasks.Last().ContinueWith(task =>
|
||||
{
|
||||
if (task.IsFaulted)
|
||||
return;
|
||||
lock (LoadedServices)
|
||||
{
|
||||
LoadedServices.Add(serviceType);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
while (tasks.Any())
|
||||
if (!tasks.Any())
|
||||
throw new InvalidOperationException("Unresolvable dependency cycle detected");
|
||||
|
||||
if (servicesToLoad.Any())
|
||||
{
|
||||
await Task.WhenAny(tasks);
|
||||
tasks.RemoveAll(x => x.IsCompleted);
|
||||
var faultedTasks = tasks.Where(x => x.IsFaulted).Select(x => (Exception)x.Exception!).ToArray();
|
||||
if (faultedTasks.Any())
|
||||
throw new AggregateException(faultedTasks);
|
||||
}
|
||||
|
||||
UnloadAllServices();
|
||||
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unloads all services, in the reverse order of load.
|
||||
/// </summary>
|
||||
public static void UnloadAllServices()
|
||||
{
|
||||
var framework = Service<Framework>.GetNullable(Service<Framework>.ExceptionPropagationMode.None);
|
||||
if (framework is { IsInFrameworkUpdateThread: false, IsFrameworkUnloading: false })
|
||||
{
|
||||
framework.RunOnFrameworkThread(UnloadAllServices).Wait();
|
||||
return;
|
||||
}
|
||||
|
||||
lock (LoadedServices)
|
||||
{
|
||||
while (LoadedServices.Any())
|
||||
else
|
||||
{
|
||||
var serviceType = LoadedServices.Last();
|
||||
LoadedServices.RemoveAt(LoadedServices.Count - 1);
|
||||
|
||||
typeof(Service<>)
|
||||
.MakeGenericType(serviceType)
|
||||
.InvokeMember(
|
||||
"Unset",
|
||||
BindingFlags.InvokeMethod | BindingFlags.Static | BindingFlags.NonPublic,
|
||||
null,
|
||||
null,
|
||||
null);
|
||||
await Task.WhenAll(tasks);
|
||||
}
|
||||
|
||||
tasks.RemoveAll(x => x.IsCompleted);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that this constructor will be called for early initialization.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Constructor)]
|
||||
[MeansImplicitUse(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)]
|
||||
public class ServiceConstructor : Attribute
|
||||
catch (Exception e)
|
||||
{
|
||||
}
|
||||
Log.Error(e, "Failed resolving services");
|
||||
try
|
||||
{
|
||||
BlockingServicesLoadedTaskCompletionSource.SetException(e);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// don't care, as this means task result/exception has already been set
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that the field is a service that should be loaded before constructing the class.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Field)]
|
||||
public class ServiceDependency : Attribute
|
||||
{
|
||||
}
|
||||
while (tasks.Any())
|
||||
{
|
||||
await Task.WhenAny(tasks);
|
||||
tasks.RemoveAll(x => x.IsCompleted);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that the class is a service.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class Service : Attribute
|
||||
{
|
||||
}
|
||||
UnloadAllServices();
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that the class is a service, and will be instantiated automatically on startup.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class EarlyLoadedService : Service
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that the class is a service, and will be instantiated automatically on startup,
|
||||
/// blocking game main thread until it completes.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class BlockingEarlyLoadedService : EarlyLoadedService
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that the method should be called when the services given in the constructor are ready.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
[MeansImplicitUse]
|
||||
public class CallWhenServicesReady : Attribute
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unloads all services, in the reverse order of load.
|
||||
/// </summary>
|
||||
public static void UnloadAllServices()
|
||||
{
|
||||
var framework = Service<Framework>.GetNullable(Service<Framework>.ExceptionPropagationMode.None);
|
||||
if (framework is { IsInFrameworkUpdateThread: false, IsFrameworkUnloading: false })
|
||||
{
|
||||
framework.RunOnFrameworkThread(UnloadAllServices).Wait();
|
||||
return;
|
||||
}
|
||||
|
||||
lock (LoadedServices)
|
||||
{
|
||||
while (LoadedServices.Any())
|
||||
{
|
||||
var serviceType = LoadedServices.Last();
|
||||
LoadedServices.RemoveAt(LoadedServices.Count - 1);
|
||||
|
||||
typeof(Service<>)
|
||||
.MakeGenericType(serviceType)
|
||||
.InvokeMember(
|
||||
"Unset",
|
||||
BindingFlags.InvokeMethod | BindingFlags.Static | BindingFlags.NonPublic,
|
||||
null,
|
||||
null,
|
||||
null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that this constructor will be called for early initialization.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Constructor)]
|
||||
[MeansImplicitUse(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)]
|
||||
public class ServiceConstructor : Attribute
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that the field is a service that should be loaded before constructing the class.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Field)]
|
||||
public class ServiceDependency : Attribute
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that the class is a service.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class Service : Attribute
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that the class is a service, and will be instantiated automatically on startup.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class EarlyLoadedService : Service
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that the class is a service, and will be instantiated automatically on startup,
|
||||
/// blocking game main thread until it completes.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class BlockingEarlyLoadedService : EarlyLoadedService
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that the method should be called when the services given in the constructor are ready.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
[MeansImplicitUse]
|
||||
public class CallWhenServicesReady : Attribute
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue