using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using Reflex.Configuration; using Reflex.Core; using Reflex.Exceptions; using Reflex.Logging; using UnityEngine; using UnityEngine.SceneManagement; using UnityEngine.Scripting; [assembly: AlwaysLinkAssembly] // https://docs.unity3d.com/ScriptReference/Scripting.AlwaysLinkAssemblyAttribute.html namespace Reflex.Injectors { internal static class UnityInjector { internal static Action OnSceneLoaded; internal static Dictionary ContainersPerScene { get; } = new(); [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterAssembliesLoaded)] private static void AfterAssembliesLoaded() { ReportReflexDebuggerStatus(); ResetStaticState(); void InjectScene(Scene scene, ContainerScope containerScope) { ReflexLogger.Log($"Scene {scene.name} ({scene.GetHashCode()}) loaded", LogLevel.Development); var sceneContainer = CreateSceneContainer(scene, containerScope); if (ContainersPerScene.TryAdd(scene, sceneContainer)) { SceneInjector.Inject(scene, sceneContainer); } else { throw new SceneHasMultipleSceneScopesException(scene); } } void DisposeScene(Scene scene) { ReflexLogger.Log($"Scene {scene.name} ({scene.GetHashCode()}) unloaded", LogLevel.Development); if (ContainersPerScene.Remove(scene, out var sceneContainer)) // Not all scenes has containers { sceneContainer.Dispose(); } } void DisposeProject() { Container.RootContainer?.Dispose(); Container.RootContainer = null; // Unsubscribe from static events ensuring that Reflex works with domain reloading set to false OnSceneLoaded -= InjectScene; SceneManager.sceneUnloaded -= DisposeScene; Application.quitting -= DisposeProject; } OnSceneLoaded += InjectScene; SceneManager.sceneUnloaded += DisposeScene; Application.quitting += DisposeProject; } private static Container CreateRootContainer() { var reflexSettings = ReflexSettings.Instance; var builder = new ContainerBuilder().SetName("RootContainer"); if (reflexSettings.RootScopes != null) { foreach (var rootScope in reflexSettings.RootScopes.Where(x => x != null && x.gameObject.activeSelf)) { rootScope.InstallBindings(builder); ReflexLogger.Log($"Root Bindings Installed from '{rootScope.name}'", LogLevel.Info, rootScope.gameObject); } } ContainerScope.OnRootContainerBuilding?.Invoke(builder); return builder.Build(); } private static Container CreateSceneContainer(Scene scene, ContainerScope containerScope) { if (Container.RootContainer == null) { Container.RootContainer = CreateRootContainer(); } return Container.RootContainer.Scope(builder => { builder.SetName($"{scene.name} ({scene.GetHashCode()})"); containerScope.InstallBindings(builder); ContainerScope.OnSceneContainerBuilding?.Invoke(scene, builder); ReflexLogger.Log($"Scene ({scene.name}) Bindings Installed", LogLevel.Info, containerScope.gameObject); }); } /// /// Ensure static state is reset. /// This is only required when playing from editor when ProjectSettings > Editor > Reload Domain is set to false. /// [Conditional("UNITY_EDITOR")] private static void ResetStaticState() { OnSceneLoaded = null; Container.RootContainer = null; ContainersPerScene.Clear(); ContainerScope.OnRootContainerBuilding = null; ContainerScope.OnSceneContainerBuilding = null; #if UNITY_EDITOR Container.RootContainers.Clear(); #endif } [Conditional("REFLEX_DEBUG")] private static void ReportReflexDebuggerStatus() { ReflexLogger.Log("Symbol REFLEX_DEBUG are defined, performance impacted!", LogLevel.Warning); } } }