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()