using UnityEditor; using UnityEngine; using UnityEngine.SceneManagement; using Core.Lifecycle; using Core.SaveLoad; using AppleHills.Core.Settings; using Bootstrap; namespace Editor.Lifecycle { /// /// Editor-only bootstrap that ensures OnSceneReady is triggered when playing directly from a scene in Unity Editor. /// /// PROBLEM: When you press Play in the editor without going through the scene manager: /// - CustomBoot runs and triggers OnBootCompletionTriggered (which broadcasts OnManagedAwake) /// - But BroadcastSceneReady is NEVER called for the initial scene /// - Components in the scene never receive their OnSceneReady() callback /// /// SOLUTION: After boot completes, detect the active scene and broadcast OnSceneReady for it. /// This only runs in editor mode and mimics what SceneManagerService does during normal scene transitions. /// [InitializeOnLoad] public static class EditorLifecycleBootstrap { private static bool hasTriggeredInitialSceneReady = false; private static int framesSincePlayMode = 0; private const int MaxFramesToWait = 300; // 5 seconds at 60fps static EditorLifecycleBootstrap() { // Subscribe to play mode state changes EditorApplication.playModeStateChanged += OnPlayModeStateChanged; } private static void OnPlayModeStateChanged(PlayModeStateChange state) { // Reset flag when exiting play mode if (state == PlayModeStateChange.ExitingPlayMode || state == PlayModeStateChange.EnteredEditMode) { hasTriggeredInitialSceneReady = false; framesSincePlayMode = 0; return; } // When we enter play mode, wait for boot to complete then trigger scene ready if (state == PlayModeStateChange.EnteredPlayMode) { hasTriggeredInitialSceneReady = false; framesSincePlayMode = 0; // Use EditorApplication.update to poll until boot completes EditorApplication.update += WaitForBootAndTriggerSceneReady; } } private static void WaitForBootAndTriggerSceneReady() { framesSincePlayMode++; // Safety timeout - if boot hasn't completed after 5 seconds, something is wrong if (framesSincePlayMode > MaxFramesToWait) { Debug.LogError($"[EditorLifecycleBootstrap] Timed out waiting for boot completion after {MaxFramesToWait} frames. " + "CustomBoot may have failed to initialize properly."); EditorApplication.update -= WaitForBootAndTriggerSceneReady; return; } // Check if boot has completed if (!CustomBoot.Initialised) return; // Check if LifecycleManager exists if (LifecycleManager.Instance == null) { Debug.LogWarning("[EditorLifecycleBootstrap] LifecycleManager instance not found. " + "Lifecycle may not be properly initialized."); EditorApplication.update -= WaitForBootAndTriggerSceneReady; return; } // Only trigger once per play session if (hasTriggeredInitialSceneReady) { EditorApplication.update -= WaitForBootAndTriggerSceneReady; return; } hasTriggeredInitialSceneReady = true; EditorApplication.update -= WaitForBootAndTriggerSceneReady; // Get the active scene Scene activeScene = SceneManager.GetActiveScene(); if (!activeScene.isLoaded) { Debug.LogWarning($"[EditorLifecycleBootstrap] Active scene '{activeScene.name}' is not loaded."); return; } // Skip bootstrap scene - it doesn't need scene ready // Note: BootstrapScene is the infrastructure scene, not a gameplay scene if (activeScene.name == "BootstrapScene" || activeScene.name == "Bootstrap") { Debug.Log($"[EditorLifecycleBootstrap] Skipping OnSceneReady for infrastructure scene: {activeScene.name}"); return; } Debug.Log($"[EditorLifecycleBootstrap] Triggering lifecycle for initial scene: {activeScene.name}"); // Broadcast scene ready for the initial scene // This mimics what SceneManagerService does during scene transitions (Phase 10) try { LifecycleManager.Instance.BroadcastSceneReady(activeScene.name); } catch (System.Exception ex) { Debug.LogError($"[EditorLifecycleBootstrap] Error broadcasting SceneReady: {ex.Message}\n{ex.StackTrace}"); return; } // Restore scene-specific data via SaveLoadManager // This mimics SceneManagerService Phase 11 if (SaveLoadManager.Instance != null) { var debugSettings = DeveloperSettingsProvider.Instance.GetSettings(); if (debugSettings.useSaveLoadSystem) { try { Debug.Log($"[EditorLifecycleBootstrap] Restoring scene data for: {activeScene.name}"); SaveLoadManager.Instance.RestoreSceneData(); } catch (System.Exception ex) { Debug.LogError($"[EditorLifecycleBootstrap] Error restoring scene data: {ex.Message}\n{ex.StackTrace}"); } } } } } }