diff --git a/Dalamud/Dalamud.cs b/Dalamud/Dalamud.cs
index 8a75049ad..f50a39aa3 100644
--- a/Dalamud/Dalamud.cs
+++ b/Dalamud/Dalamud.cs
@@ -74,14 +74,17 @@ internal sealed class Dalamud : IServiceType
if (!configuration.IsResumeGameAfterPluginLoad)
{
NativeFunctions.SetEvent(mainThreadContinueEvent);
- try
- {
- _ = ServiceManager.InitializeEarlyLoadableServices();
- }
- catch (Exception e)
- {
- Log.Error(e, "Service initialization failure");
- }
+ ServiceManager.InitializeEarlyLoadableServices()
+ .ContinueWith(t =>
+ {
+ if (t.IsCompletedSuccessfully)
+ return;
+
+ Log.Error(t.Exception!, "Service initialization failure");
+ Util.Fatal(
+ "Dalamud failed to load all necessary services.\n\nThe game will continue, but you may not be able to use plugins.",
+ "Dalamud", false);
+ });
}
else
{
diff --git a/Dalamud/Interface/Internal/InterfaceManager.cs b/Dalamud/Interface/Internal/InterfaceManager.cs
index dd85f1db4..93d9bb1dd 100644
--- a/Dalamud/Interface/Internal/InterfaceManager.cs
+++ b/Dalamud/Interface/Internal/InterfaceManager.cs
@@ -1276,6 +1276,7 @@ internal class InterfaceManager : IDisposable, IServiceType
///
/// Represents an instance of InstanceManager with scene ready for use.
///
+ [ServiceManager.Service]
public class InterfaceManagerWithScene : IServiceType
{
///
diff --git a/Dalamud/ServiceManager.cs b/Dalamud/ServiceManager.cs
index e8512e854..553e0a4af 100644
--- a/Dalamud/ServiceManager.cs
+++ b/Dalamud/ServiceManager.cs
@@ -47,9 +47,9 @@ internal static class ServiceManager
None = 0,
///
- /// Regular service.
+ /// Service that is loaded manually.
///
- ManualService = 1 << 0,
+ ProvidedService = 1 << 0,
///
/// Service that is loaded asynchronously while the game starts.
@@ -90,7 +90,7 @@ internal static class ServiceManager
{
void ProvideService(T service) where T : IServiceType
{
- Debug.Assert(typeof(T).GetServiceKind().HasFlag(ServiceKind.ManualService), "Provided service must have Service attribute");
+ Debug.Assert(typeof(T).GetServiceKind().HasFlag(ServiceKind.ProvidedService), "Provided service must have Service attribute");
Service.Provide(service);
LoadedServices.Add(typeof(T));
}
@@ -119,23 +119,18 @@ internal static class ServiceManager
var serviceContainer = Service.Get();
- foreach (var serviceType in Assembly.GetExecutingAssembly().GetTypes())
+ foreach (var serviceType in Assembly.GetExecutingAssembly().GetTypes().Where(x => x.IsAssignableTo(typeof(IServiceType)) && !x.IsInterface && !x.IsAbstract))
{
var serviceKind = serviceType.GetServiceKind();
if (serviceKind is ServiceKind.None)
- continue;
+ throw new Exception($"Service<{serviceType.FullName}> did not specify a kind");
// Let IoC know about the interfaces this service implements
serviceContainer.RegisterInterfaces(serviceType);
// Scoped service do not go through Service and are never early loaded
- // Manual services are provided
- if (serviceKind.HasFlag(ServiceKind.ScopedService) || serviceKind.HasFlag(ServiceKind.ManualService))
+ if (serviceKind.HasFlag(ServiceKind.ScopedService))
continue;
-
- Debug.Assert(
- !serviceKind.HasFlag(ServiceKind.ManualService) && !serviceKind.HasFlag(ServiceKind.ScopedService),
- "Regular and scoped services should never be loaded early");
var genericWrappedServiceType = typeof(Service<>).MakeGenericType(serviceType);
@@ -147,9 +142,14 @@ internal static class ServiceManager
null,
null);
+ getAsyncTaskMap[serviceType] = getTask;
+
+ // We don't actually need to load provided services, something else does
+ if (serviceKind.HasFlag(ServiceKind.ProvidedService))
+ continue;
+
if (serviceKind.HasFlag(ServiceKind.BlockingEarlyLoadedService))
{
- getAsyncTaskMap[serviceType] = getTask;
blockingEarlyLoadingServices.Add(serviceType);
}
else
@@ -191,13 +191,13 @@ internal static class ServiceManager
var hasDeps = true;
foreach (var dependency in dependencyServicesMap[serviceType])
{
- var depServiceKind = dependency.GetServiceKind();
- var depResolveTask = getAsyncTaskMap.GetValueOrDefault(dependency);
+ var depUnderlyingServiceType = dependency.GetGenericArguments().First();
+ var depResolveTask = getAsyncTaskMap.GetValueOrDefault(depUnderlyingServiceType);
- if (depResolveTask == null && (depServiceKind.HasFlag(ServiceKind.EarlyLoadedService) || depServiceKind.HasFlag(ServiceKind.BlockingEarlyLoadedService)))
+ if (depResolveTask == null)
{
- Log.Error("{Type}: {Dependency} has no resolver task, is it early loaded or blocking early loaded?", serviceType.FullName!, dependency.FullName!);
- Debug.Assert(false, $"No resolver for dependent service {dependency.FullName}");
+ Log.Error("{Type}: {Dependency} has no resolver task", serviceType.FullName!, dependency.FullName!);
+ Debug.Assert(false, $"No resolver for dependent service {depUnderlyingServiceType.FullName}");
}
else if (depResolveTask is { IsCompleted: false })
{
@@ -386,7 +386,7 @@ internal static class ServiceManager
if (attr.IsAssignableTo(typeof(ScopedService)))
return ServiceKind.ScopedService;
- return ServiceKind.ManualService;
+ return ServiceKind.ProvidedService;
}
///
diff --git a/Dalamud/Service{T}.cs b/Dalamud/Service{T}.cs
index 539941c27..b609c9082 100644
--- a/Dalamud/Service{T}.cs
+++ b/Dalamud/Service{T}.cs
@@ -123,6 +123,8 @@ internal static class Service where T : IServiceType
public static List GetDependencyServices()
{
var res = new List();
+
+ ServiceManager.Log.Verbose("Service<{0}>: Getting dependencies", typeof(T).Name);
var ctor = GetServiceConstructor();
if (ctor != null)
@@ -181,6 +183,11 @@ internal static class Service where T : IServiceType
res.Add(serviceType);
}
}
+
+ foreach (var type in res)
+ {
+ ServiceManager.Log.Verbose("Service<{0}>: => Dependency: {1}", typeof(T).Name, type.Name);
+ }
return res
.Distinct()