Files
AppleHillsProduction/docs/bootstrapped_manager_initialization_review.md
2025-11-05 20:37:16 +01:00

8.9 KiB

Bootstrapped Manager Initialization Review - Summary

Overview

Performed a comprehensive review of all bootstrapped singleton managers to ensure critical initialization happens in Awake() rather than OnManagedAwake(), making infrastructure available when other components' OnManagedAwake() runs.

Key Principle

Awake() = Infrastructure Setup

  • Singleton instance registration
  • Critical service initialization (settings, scene tracking, input setup)
  • Component configuration
  • State that OTHER components depend on

OnManagedAwake() = Dependent Initialization

  • Initialization that depends on OTHER managers
  • Event subscriptions to other managers
  • Non-critical setup

Managers Updated

1. GameManager (Priority 10)

Moved to Awake():

  • Settings provider creation
  • Settings initialization (InitializeSettings, InitializeDeveloperSettings)
  • Verbosity settings loading

Rationale: Other managers need settings in their OnManagedAwake(). Settings MUST be available early.

Impact: All other managers can now safely call GameManager.GetSettingsObject<T>() in their OnManagedAwake().

private new void Awake()
{
    _instance = this;
    
    // Create settings providers - CRITICAL for other managers
    SettingsProvider.Instance.gameObject.name = "Settings Provider";
    DeveloperSettingsProvider.Instance.gameObject.name = "Developer Settings Provider";

    // Load all settings synchronously - CRITICAL infrastructure
    InitializeSettings();
    InitializeDeveloperSettings();
    
    _settingsLogVerbosity = DeveloperSettingsProvider.Instance.GetSettings<DebugSettings>().settingsLogVerbosity;
    _managerLogVerbosity = DeveloperSettingsProvider.Instance.GetSettings<DebugSettings>().gameManagerLogVerbosity;
}

2. SceneManagerService (Priority 15)

Moved to Awake():

  • Scene tracking initialization (InitializeCurrentSceneTracking)
  • Bootstrap scene loading

Kept in OnManagedAwake():

  • Loading screen reference (depends on LoadingScreenController instance)
  • Event setup (depends on loading screen)
  • Verbosity settings

Rationale: Scene tracking is critical state. Loading screen setup depends on LoadingScreenController's instance being set first.

private new void Awake()
{
    _instance = this;
    
    // Initialize current scene tracking - CRITICAL for scene management
    InitializeCurrentSceneTracking();
    
    // Ensure BootstrapScene is loaded
    var bootstrap = SceneManager.GetSceneByName(BootstrapSceneName);
    if (!bootstrap.isLoaded)
    {
        SceneManager.LoadScene(BootstrapSceneName, LoadSceneMode.Additive);
    }
}

protected override void OnManagedAwake()
{
    // DEPENDS on LoadingScreenController instance
    _loadingScreen = LoadingScreenController.Instance;
    SetupLoadingScreenEvents();
    
    _logVerbosity = DeveloperSettingsProvider.Instance.GetSettings<DebugSettings>().sceneLogVerbosity;
}

3. InputManager (Priority 25)

Moved to Awake():

  • Verbosity settings loading
  • Settings reference initialization
  • PlayerInput component setup
  • Input action subscriptions
  • Initial input mode setup

Kept in OnManagedAwake():

  • SceneManagerService event subscriptions (depends on SceneManagerService instance)

Rationale: Input system MUST be functional immediately. Event subscriptions to other managers wait until OnManagedAwake().

private new void Awake()
{
    _instance = this;
    
    // Load settings early (GameManager sets these up in its Awake)
    _logVerbosity = DeveloperSettingsProvider.Instance.GetSettings<DebugSettings>().inputLogVerbosity;
    _interactionSettings = GameManager.GetSettingsObject<IInteractionSettings>();
    
    // Set up PlayerInput component and actions - CRITICAL for input to work
    playerInput = GetComponent<PlayerInput>();
    // ... set up actions and subscriptions
    
    // Initialize input mode for current scene
    SwitchInputOnSceneLoaded(SceneManager.GetActiveScene().name);
}

protected override void OnManagedAwake()
{
    // DEPENDS on SceneManagerService instance
    if (SceneManagerService.Instance != null)
    {
        SceneManagerService.Instance.SceneLoadCompleted += OnSceneLoadCompleted;
    }
}

4. SaveLoadManager (Priority 20)

Moved to Awake():

  • Critical state initialization (IsSaveDataLoaded, IsRestoringState)

Kept in OnManagedAwake():

  • Discovery of saveables
  • Loading save data (depends on settings)

Rationale: State flags should be initialized immediately. Discovery and loading depend on settings and scene state.

private new void Awake()
{
    _instance = this;
    
    // Initialize critical state immediately
    IsSaveDataLoaded = false;
    IsRestoringState = false;
}

protected override void OnManagedAwake()
{
    // Discovery and loading depend on settings and scene state
    DiscoverInactiveSaveables("RestoreInEditor");
    if (DeveloperSettingsProvider.Instance.GetSettings<DebugSettings>().useSaveLoadSystem)
    {
        Load();
    }
}

5. LoadingScreenController (Priority 45)

Moved to Awake():

  • Container reference setup
  • Initial state (hidden, inactive)

Rationale: SceneManagerService (priority 15) needs to access LoadingScreenController.Instance in its OnManagedAwake(). The loading screen MUST be properly configured before that.

private new void Awake()
{
    _instance = this;
    
    // Set up container reference early
    if (loadingScreenContainer == null)
        loadingScreenContainer = gameObject;
    
    // Ensure the loading screen is initially hidden
    if (loadingScreenContainer != null)
    {
        loadingScreenContainer.SetActive(false);
    }
}

6. PauseMenu (Priority 55)

Moved to Awake():

  • CanvasGroup component setup
  • Initial state (hidden, non-interactive)

Kept in OnSceneReady():

  • Event subscriptions to SceneManagerService and UIPageController

Rationale: Component configuration should happen immediately. Event subscriptions wait for scene ready.

7. SceneOrientationEnforcer (Priority 70)

Moved to Awake():

  • Verbosity settings loading
  • Scene loaded event subscription
  • Initial orientation enforcement

Rationale: Orientation enforcement should start immediately when scenes load.

Critical Dependencies Resolved

Before This Review

SceneManagerService.OnManagedAwake() → tries to access LoadingScreenController.Instance
                                      → NULL if LoadingScreenController.OnManagedAwake() hasn't run yet!

After This Review

All Awake() methods run (order doesn't matter):
  - GameManager.Awake() → Sets up settings
  - SceneManagerService.Awake() → Sets up scene tracking
  - InputManager.Awake() → Sets up input system
  - LoadingScreenController.Awake() → Sets up loading screen
  - etc.

Then OnManagedAwake() runs in priority order:
  - GameManager.OnManagedAwake() (10)
  - SceneManagerService.OnManagedAwake() (15) → LoadingScreenController.Instance is GUARANTEED available
  - InputManager.OnManagedAwake() (25) → SceneManagerService.Instance is GUARANTEED available
  - etc.

Design Guidelines Established

What Goes in Awake()

  1. Singleton instance assignment (_instance = this)
  2. Critical infrastructure (settings, scene tracking)
  3. Component setup (GetComponent, initial state)
  4. State initialization that others depend on
  5. Subscriptions to Unity events (SceneManager.sceneLoaded)

What Goes in OnManagedAwake()

  1. Event subscriptions to OTHER managers
  2. Initialization that depends on settings
  3. Non-critical setup
  4. Logging (depends on settings)

What Stays in OnSceneReady()

  1. Scene-specific initialization
  2. Event subscriptions that are scene-dependent

Compilation Status

No compilation errors ⚠️ Only pre-existing naming convention warnings

Impact

Before

  • Race conditions possible if managers accessed each other's instances
  • Settings might not be available when needed
  • Input system might not be configured when accessed
  • Loading screen might not be set up when SceneManagerService needs it

After

  • All critical infrastructure guaranteed available in OnManagedAwake()
  • Settings always available for all managers
  • Input system always functional
  • Loading screen always configured
  • Clean separation: Awake = infrastructure, OnManagedAwake = orchestration

Testing Recommendations

  1. Test boot sequence from StartingScene
  2. Test level switching via LevelSwitch
  3. Verify loading screen shows correctly
  4. Verify input works after scene loads
  5. Check console for any null reference exceptions

Files Modified

  1. GameManager.cs
  2. SceneManagerService.cs
  3. InputManager.cs
  4. SaveLoadManager.cs
  5. LoadingScreenController.cs
  6. PauseMenu.cs
  7. SceneOrientationEnforcer.cs

Status: COMPLETE - All bootstrapped managers properly initialized with correct Awake/OnManagedAwake separation