Rework of base interactables and managed behaviors
This commit is contained in:
166
docs/critical_bugfix_missing_base_awake.md
Normal file
166
docs/critical_bugfix_missing_base_awake.md
Normal file
@@ -0,0 +1,166 @@
|
||||
# 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!
|
||||
|
||||
Reference in New Issue
Block a user