using System; using UnityEngine; using VContainer.Internal; #if VCONTAINER_ECS_INTEGRATION using Unity.Entities; #endif namespace VContainer.Unity { public readonly struct EntryPointsBuilder { public static void EnsureDispatcherRegistered(IContainerBuilder containerBuilder) { if (containerBuilder.Exists(typeof(EntryPointDispatcher), false)) return; containerBuilder.Register(Lifetime.Scoped); containerBuilder.RegisterEntryPointExceptionHandler(UnityEngine.Debug.LogException); containerBuilder.RegisterBuildCallback(container => { container.Resolve().Dispatch(); }); } readonly IContainerBuilder containerBuilder; readonly Lifetime lifetime; public EntryPointsBuilder(IContainerBuilder containerBuilder, Lifetime lifetime) { this.containerBuilder = containerBuilder; this.lifetime = lifetime; } public RegistrationBuilder Add() => containerBuilder.Register(lifetime).AsImplementedInterfaces(); public void OnException(Action exceptionHandler) => containerBuilder.RegisterEntryPointExceptionHandler(exceptionHandler); } public readonly struct ComponentsBuilder { readonly IContainerBuilder containerBuilder; readonly Transform parentTransform; public ComponentsBuilder(IContainerBuilder containerBuilder, Transform parentTransform = null) { this.containerBuilder = containerBuilder; this.parentTransform = parentTransform; } public RegistrationBuilder AddInstance(TInterface component) { return containerBuilder.RegisterComponent(component); } public ComponentRegistrationBuilder AddInHierarchy() => containerBuilder.RegisterComponentInHierarchy() .UnderTransform(parentTransform); public ComponentRegistrationBuilder AddOnNewGameObject(Lifetime lifetime, string newGameObjectName = null) where T : Component => containerBuilder.RegisterComponentOnNewGameObject(lifetime, newGameObjectName) .UnderTransform(parentTransform); public ComponentRegistrationBuilder AddInNewPrefab(T prefab, Lifetime lifetime) where T : Component => containerBuilder.RegisterComponentInNewPrefab(prefab, lifetime) .UnderTransform(parentTransform); } public static class ContainerBuilderUnityExtensions { public static void UseEntryPoints( this IContainerBuilder builder, Action configuration) { builder.UseEntryPoints(Lifetime.Singleton, configuration); } public static void UseEntryPoints( this IContainerBuilder builder, Lifetime lifetime, Action configuration) { EntryPointsBuilder.EnsureDispatcherRegistered(builder); configuration(new EntryPointsBuilder(builder, lifetime)); } public static void UseComponents(this IContainerBuilder builder, Action configuration) { configuration(new ComponentsBuilder(builder)); } public static void UseComponents( this IContainerBuilder builder, Transform root, Action configuration) { configuration(new ComponentsBuilder(builder, root)); } public static RegistrationBuilder RegisterEntryPoint( this IContainerBuilder builder, Lifetime lifetime = Lifetime.Singleton) { EntryPointsBuilder.EnsureDispatcherRegistered(builder); return builder.Register(lifetime).AsImplementedInterfaces(); } public static RegistrationBuilder RegisterEntryPoint( this IContainerBuilder builder, Func implementationConfiguration, Lifetime lifetime) { EntryPointsBuilder.EnsureDispatcherRegistered(builder); return builder.Register(new FuncRegistrationBuilder(container => implementationConfiguration(container), typeof(TInterface), lifetime)).AsImplementedInterfaces(); } public static void RegisterEntryPointExceptionHandler( this IContainerBuilder builder, Action exceptionHandler) { builder.Register(c => new EntryPointExceptionHandler(exceptionHandler), Lifetime.Scoped); } public static RegistrationBuilder RegisterComponent( this IContainerBuilder builder, TInterface component) { var registrationBuilder = new ComponentRegistrationBuilder(component).As(typeof(TInterface)); // Force inject execution builder.RegisterBuildCallback(container => container.Resolve()); return builder.Register(registrationBuilder); } public static ComponentRegistrationBuilder RegisterComponentInHierarchy( this IContainerBuilder builder, Type type) { var lifetimeScope = (LifetimeScope)builder.ApplicationOrigin; var scene = lifetimeScope.gameObject.scene; var registrationBuilder = new ComponentRegistrationBuilder(scene, type); // Force inject execution builder.RegisterBuildCallback( container => { container.Resolve( registrationBuilder.InterfaceTypes != null ? registrationBuilder.InterfaceTypes[0] : registrationBuilder.ImplementationType ); } ); return builder.Register(registrationBuilder); } public static ComponentRegistrationBuilder RegisterComponentInHierarchy(this IContainerBuilder builder) { return builder.RegisterComponentInHierarchy(typeof(T)); } public static ComponentRegistrationBuilder RegisterComponentOnNewGameObject( this IContainerBuilder builder, Type type, Lifetime lifetime, string newGameObjectName = null) { return builder.Register(new ComponentRegistrationBuilder(newGameObjectName, type, lifetime)); } public static ComponentRegistrationBuilder RegisterComponentOnNewGameObject( this IContainerBuilder builder, Lifetime lifetime, string newGameObjectName = null) where T : Component { return builder.RegisterComponentOnNewGameObject(typeof(T), lifetime, newGameObjectName); } public static ComponentRegistrationBuilder RegisterComponentInNewPrefab( this IContainerBuilder builder, Type interfaceType, Component prefab, Lifetime lifetime) { var componentRegistrationBuilder = builder.Register(new ComponentRegistrationBuilder(_ => prefab, prefab.GetType(), lifetime)); componentRegistrationBuilder.As(interfaceType); return componentRegistrationBuilder; } public static ComponentRegistrationBuilder RegisterComponentInNewPrefab( this IContainerBuilder builder, T prefab, Lifetime lifetime) where T : Component { return builder.RegisterComponentInNewPrefab(typeof(T), prefab, lifetime); } public static ComponentRegistrationBuilder RegisterComponentInNewPrefab( this IContainerBuilder builder, Func prefab, Lifetime lifetime) where T : Component { return builder.Register(new ComponentRegistrationBuilder(prefab, typeof(T), lifetime)); } public static ComponentRegistrationBuilder RegisterComponentInNewPrefab( this IContainerBuilder builder, Func prefab, Lifetime lifetime) where TImplement : Component, TInterface { var componentRegistrationBuilder = builder.Register(new ComponentRegistrationBuilder(prefab, typeof(TImplement), lifetime)); componentRegistrationBuilder.As(); return componentRegistrationBuilder; } #if VCONTAINER_ECS_INTEGRATION public readonly struct NewWorldBuilder { readonly IContainerBuilder containerBuilder; readonly string worldName; readonly Lifetime worldLifetime; public NewWorldBuilder(IContainerBuilder containerBuilder, string worldName, Lifetime worldLifetime) { this.containerBuilder = containerBuilder; this.worldName = worldName; this.worldLifetime = worldLifetime; containerBuilder.RegisterNewWorld(worldName, worldLifetime); } public SystemRegistrationBuilder Add() where T : ComponentSystemBase => containerBuilder.RegisterSystemIntoWorld(worldName); #if UNITY_2022_2_OR_NEWER public UnmanagedSystemRegistrationBuilder AddUnmanaged(T system) where T : unmanaged, ISystem => containerBuilder.RegisterUnmanagedSystemIntoWorld(worldName); #endif } public readonly struct DefaultWorldBuilder { readonly IContainerBuilder containerBuilder; public DefaultWorldBuilder(IContainerBuilder containerBuilder) { this.containerBuilder = containerBuilder; } public RegistrationBuilder Add() where T : ComponentSystemBase => containerBuilder.RegisterSystemFromDefaultWorld(); #if UNITY_2022_2_OR_NEWER public RegistrationBuilder AddUnmanaged() where T : unmanaged, ISystem => containerBuilder.RegisterUnmanagedSystemFromDefaultWorld(); #endif } // Use exisiting world public static void UseDefaultWorld(this IContainerBuilder builder, Action configuration) { var systems = new DefaultWorldBuilder(builder); configuration(systems); } public static RegistrationBuilder RegisterSystemFromDefaultWorld(this IContainerBuilder builder) where T : ComponentSystemBase => RegisterSystemFromWorld(builder, World.DefaultGameObjectInjectionWorld); #if UNITY_2022_2_OR_NEWER public static RegistrationBuilder RegisterUnmanagedSystemFromDefaultWorld(this IContainerBuilder builder) where T : unmanaged, ISystem => RegisterUnmanagedSystemFromWorld(builder, World.DefaultGameObjectInjectionWorld); #endif public static RegistrationBuilder RegisterSystemFromWorld(this IContainerBuilder builder, World world) where T : ComponentSystemBase { #if UNITY_2022_2_OR_NEWER var system = world.GetExistingSystemManaged(); #else var system = world.GetExistingSystem(); #endif if (system is null) throw new ArgumentException($"{typeof(T).FullName} is not in the world {world}"); return builder.RegisterComponent(system) .As(typeof(ComponentSystemBase), typeof(T)); } #if UNITY_2022_2_OR_NEWER public static RegistrationBuilder RegisterUnmanagedSystemFromWorld(this IContainerBuilder builder, World world) where T : unmanaged, ISystem { var system = world.Unmanaged.GetExistingUnmanagedSystem(); if (system == SystemHandle.Null) throw new ArgumentException($"{typeof(T).FullName} is not in the world {world}"); Type refType = typeof(UnmanagedSystemReference<>); Type target = refType.MakeGenericType(typeof(T)); var reference = (UnmanagedSystemReference)Activator.CreateInstance(target, system, world); return builder.RegisterComponent(reference) .As(target); } #endif // Use custom world public static void UseNewWorld( this IContainerBuilder builder, string worldName, Lifetime lifetime, Action configuration) { var systems = new NewWorldBuilder(builder, worldName, lifetime); configuration(systems); } public static RegistrationBuilder RegisterNewWorld( this IContainerBuilder builder, string worldName, Lifetime lifetime, Action configuration = null) { builder.Register(lifetime) .WithParameter(typeof(string), worldName); return builder.Register(new WorldRegistrationBuilder(worldName, lifetime, configuration)); } public static SystemRegistrationBuilder RegisterSystemIntoWorld( this IContainerBuilder builder, string worldName) where T : ComponentSystemBase { var registrationBuilder = new SystemRegistrationBuilder(typeof(T), worldName) .IntoGroup(); return builder.Register(registrationBuilder); } public static SystemRegistrationBuilder RegisterSystemIntoWorld( this IContainerBuilder builder, string worldName) where T : ComponentSystemBase where T1 : ComponentSystemGroup { var registrationBuilder = new SystemRegistrationBuilder(typeof(T), worldName) .IntoGroup(); return builder.Register(registrationBuilder); } #if UNITY_2022_2_OR_NEWER public static UnmanagedSystemRegistrationBuilder RegisterUnmanagedSystemIntoWorld( this IContainerBuilder builder, string worldName) where T : unmanaged, ISystem { var registrationBuilder = new UnmanagedSystemRegistrationBuilder(typeof(T), worldName) .IntoGroup(); return builder.Register(registrationBuilder); } public static UnmanagedSystemRegistrationBuilder RegisterUnmanagedSystemIntoWorld( this IContainerBuilder builder, string worldName) where T : unmanaged, ISystem where T1 : ComponentSystemGroup { var registrationBuilder = new UnmanagedSystemRegistrationBuilder(typeof(T), worldName) .IntoGroup(); return builder.Register(registrationBuilder); } #endif public static SystemRegistrationBuilder RegisterSystemIntoDefaultWorld(this IContainerBuilder builder) where T : ComponentSystemBase { var registrationBuilder = new SystemRegistrationBuilder(typeof(T), null) .IntoGroup(); return builder.Register(registrationBuilder); } public static SystemRegistrationBuilder RegisterSystemIntoDefaultWorld(this IContainerBuilder builder) where T : ComponentSystemBase where T1 : ComponentSystemGroup { var registrationBuilder = new SystemRegistrationBuilder(typeof(T), null) .IntoGroup(); return builder.Register(registrationBuilder); } #if UNITY_2022_2_OR_NEWER public static UnmanagedSystemRegistrationBuilder RegisterUnmanagedSystemIntoDefaultWorld(this IContainerBuilder builder) where T : unmanaged, ISystem { var registrationBuilder = new UnmanagedSystemRegistrationBuilder(typeof(T), null) .IntoGroup(); return builder.Register(registrationBuilder); } public static UnmanagedSystemRegistrationBuilder RegisterUnmanagedSystemIntoDefaultWorld(this IContainerBuilder builder) where T : unmanaged, ISystem where T1 : ComponentSystemGroup { var registrationBuilder = new UnmanagedSystemRegistrationBuilder(typeof(T), null) .IntoGroup(); return builder.Register(registrationBuilder); } #endif #endif } }