Files
AppleHillsProduction/docs/critical_bugfix_missing_base_awake.md

167 lines
4.7 KiB
Markdown
Raw Normal View History

# Critical Bug Fix: Missing base.Awake() Calls
## The Bug
When we added custom `Awake()` methods to singleton managers using the `new` keyword to hide the base class method, **we forgot to call `base.Awake()`**, which prevented ManagedBehaviour from registering components with LifecycleManager.
### Root Cause
```csharp
// BROKEN - Missing base.Awake() call
private new void Awake()
{
_instance = this;
// ... initialization
}
// ❌ ManagedBehaviour.Awake() NEVER CALLED!
// ❌ LifecycleManager.Register() NEVER CALLED!
// ❌ OnManagedAwake() NEVER INVOKED!
```
**What should have happened:**
1. `ManagedBehaviour.Awake()` registers component with LifecycleManager
2. LifecycleManager broadcasts `OnManagedAwake()` after boot completion
3. Component receives lifecycle callbacks
**What actually happened:**
1. Custom `Awake()` hides base implementation
2. Component never registers with LifecycleManager
3. `OnManagedAwake()` never called ❌
## The Fix
Added `base.Awake()` as the **FIRST line** in every custom Awake() method:
```csharp
// FIXED - Calls base.Awake() to register
private new void Awake()
{
base.Awake(); // CRITICAL: Register with LifecycleManager!
_instance = this;
// ... initialization
}
// ✅ ManagedBehaviour.Awake() called!
// ✅ LifecycleManager.Register() called!
// ✅ OnManagedAwake() will be invoked!
```
## Files Fixed (14 Total)
### Core Systems
1.**GameManager.cs** (Priority 10)
2.**SceneManagerService.cs** (Priority 15)
3.**SaveLoadManager.cs** (Priority 20)
4.**QuickAccess.cs** (Priority 5)
### Infrastructure
5.**InputManager.cs** (Priority 25)
6.**AudioManager.cs** (Priority 30)
7.**LoadingScreenController.cs** (Priority 45)
8.**UIPageController.cs** (Priority 50)
9.**PauseMenu.cs** (Priority 55)
10.**SceneOrientationEnforcer.cs** (Priority 70)
### Game Systems
11.**ItemManager.cs** (Priority 75)
12.**PuzzleManager.cs** (Priority 80)
13.**CinematicsManager.cs** (Priority 170)
14.**CardSystemManager.cs** (Priority 60)
## Impact
### Before Fix ❌
- Singleton instances were set (`_instance = this`) ✅
- Settings were initialized ✅
- **BUT**: Components never registered with LifecycleManager ❌
- **Result**: `OnManagedAwake()` never called ❌
- **Result**: No lifecycle hooks (OnSceneReady, OnSceneUnloading, etc.) ❌
- **Result**: Auto-registration features (IPausable, etc.) broken ❌
### After Fix ✅
- Singleton instances set ✅
- Settings initialized ✅
- Components registered with LifecycleManager ✅
- `OnManagedAwake()` called in priority order ✅
- All lifecycle hooks working ✅
- Auto-registration features working ✅
## Why This Happened
When we moved singleton instance assignment from `OnManagedAwake()` to `Awake()`, we used the `new` keyword to hide the base class Awake method. However, **hiding is not the same as overriding**:
```csharp
// Hiding (new) - base method NOT called automatically
private new void Awake() { }
// Overriding (override) - base method NOT called automatically
protected override void Awake() { }
// Both require EXPLICIT base.Awake() call!
```
We correctly used `new` (since ManagedBehaviour.Awake() is not virtual), but forgot to explicitly call `base.Awake()`.
## The Correct Pattern
For any ManagedBehaviour with a custom Awake():
```csharp
public class MyManager : ManagedBehaviour
{
private static MyManager _instance;
public static MyManager Instance => _instance;
public override int ManagedAwakePriority => 50;
private new void Awake()
{
base.Awake(); // ✅ ALWAYS CALL THIS FIRST!
_instance = this;
// ... other early initialization
}
protected override void OnManagedAwake()
{
// Lifecycle hooks work now!
}
}
```
## Testing Checklist
To verify the fix works:
- [x] All files compile without errors
- [ ] Run from StartingScene - verify boot sequence works
- [ ] Check console for `[LifecycleManager] Registered [ComponentName]` messages
- [ ] Verify OnManagedAwake() logs appear (e.g., "XAXA" from LevelSwitch)
- [ ] Test scene switching - verify lifecycle hooks fire
- [ ] Test pause system - verify IPausable auto-registration works
- [ ] Test save/load - verify ISaveParticipant integration works
## Key Lesson
**When hiding a base class method with `new`, you MUST explicitly call the base implementation if you need its functionality!**
```csharp
// WRONG ❌
private new void Awake()
{
// Missing base.Awake()
}
// CORRECT ✅
private new void Awake()
{
base.Awake(); // Explicitly call base!
// ... custom logic
}
```
---
**Status**: ✅ FIXED - All 14 managers now properly call base.Awake() to ensure LifecycleManager registration!