Files
AppleHillsProduction/Assets/Editor/Lifecycle/EditorLifecycleBootstrap.cs
tschesky 0aa2270e1a Lifecycle System Refactor & Logging Centralization (#56)
## ManagedBehaviour System Refactor

- **Sealed `Awake()`** to prevent override mistakes that break singleton registration
- **Added `OnManagedAwake()`** for early initialization (fires during registration)
- **Renamed lifecycle hook:** `OnManagedAwake()` → `OnManagedStart()` (fires after boot, mirrors Unity's Awake→Start)
- **40 files migrated** to new pattern (2 core, 38 components)
- Eliminated all fragile `private new void Awake()` patterns
- Zero breaking changes - backward compatible

## Centralized Logging System

- **Automatic tagging** via `CallerMemberName` and `CallerFilePath` - logs auto-tagged as `[ClassName][MethodName] message`
- **Unified API:** Single `Logging.Debug/Info/Warning/Error()` replaces custom `LogDebugMessage()` implementations
- **~90 logging call sites** migrated across 10 files
- **10 redundant helper methods** removed
- All logs broadcast via `Logging.OnLogEntryAdded` event for real-time monitoring

## Custom Log Console (Editor Window)

- **Persistent filter popups** for multi-selection (classes, methods, log levels) - windows stay open during selection
- **Search** across class names, methods, and message content
- **Time range filter** with MinMaxSlider
- **Export** filtered logs to timestamped `.txt` files
- **Right-click context menu** for quick filtering and copy actions
- **Visual improvements:** White text, alternating row backgrounds, color-coded log levels
- **Multiple instances** supported for simultaneous system monitoring
- Open via `AppleHills > Custom Log Console`

Co-authored-by: Michal Pikulski <michal@foolhardyhorizons.com>
Co-authored-by: Michal Pikulski <michal.a.pikulski@gmail.com>
Reviewed-on: #56
2025-11-11 08:48:29 +00:00

145 lines
5.9 KiB
C#

using UnityEditor;
using UnityEngine;
using UnityEngine.SceneManagement;
using Core.Lifecycle;
using Core.SaveLoad;
using AppleHills.Core.Settings;
using Bootstrap;
namespace Editor.Lifecycle
{
/// <summary>
/// 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 OnManagedStart)
/// - 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.
/// </summary>
[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($"<color=cyan>[EditorLifecycleBootstrap] Triggering lifecycle for initial scene: {activeScene.name}</color>");
// 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<DebugSettings>();
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}");
}
}
}
}
}
}