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

3.4 KiB

Save System Architecture

Project: AppleHills
Date: November 3, 2025


Critical Decision: All Save Data is Level-Specific

IMPORTANT: In AppleHills, ALL save data is level-specific. There is no global persistent save data that carries across all levels.

What This Means

  • DO NOT use ISaveParticipant pattern for new components
  • DO use OnSaveRequested() / OnRestoreRequested() lifecycle hooks instead
  • All save/load operations are tied to the current level/scene

Migration Impact

For existing components that implement ISaveParticipant:

  • Remove ISaveParticipant interface implementation
  • Remove SaveLoadManager.RegisterParticipant() calls
  • Add OnSaveRequested() override to save level-specific state
  • Add OnRestoreRequested() override to restore level-specific state
  • Use SaveLoadManager's existing save/load system within these hooks

Examples

OLD Pattern (Don't use)

public class PuzzleManager : MonoBehaviour, ISaveParticipant
{
    void Awake()
    {
        BootCompletionService.RegisterInitAction(() => {
            SaveLoadManager.Instance.RegisterParticipant(this);
        });
    }
    
    public string GetSaveId() => "PuzzleManager";
    
    public string Save()
    {
        return JsonUtility.ToJson(puzzleState);
    }
    
    public void Restore(string data)
    {
        puzzleState = JsonUtility.FromJson<PuzzleState>(data);
    }
}

NEW Pattern (Use this)

public class PuzzleManager : ManagedBehaviour
{
    protected override int ManagedAwakePriority => 120;
    
    protected override void OnSaveRequested()
    {
        // Save level-specific puzzle state
        string saveData = JsonUtility.ToJson(puzzleState);
        SaveLoadManager.Instance.SaveLevelData("PuzzleManager", saveData);
    }
    
    protected override void OnRestoreRequested()
    {
        // Restore level-specific puzzle state
        string saveData = SaveLoadManager.Instance.LoadLevelData("PuzzleManager");
        if (!string.IsNullOrEmpty(saveData))
        {
            puzzleState = JsonUtility.FromJson<PuzzleState>(saveData);
        }
    }
}

Components Affected by This Decision

All components that currently use ISaveParticipant:

  1. PuzzleManager - puzzle state per level
  2. PlayerTouchController - player position/state per level
  3. FollowerController - follower state per level
  4. CardSystemManager - card collection per level
  5. SaveableInteractable - interactable state per level
  6. AppleMachine - apple machine state per level

All of these will be migrated to use OnSaveRequested/OnRestoreRequested instead of ISaveParticipant.


SaveLoadManager Integration

The SaveLoadManager will continue to work as before, but components will interact with it through lifecycle hooks:

Lifecycle Flow

  1. Before scene unloads: LifecycleManager.BroadcastSaveRequested(sceneName)
    • All ManagedBehaviours in that scene get OnSaveRequested() called
    • Each component saves its level-specific data via SaveLoadManager
  2. SaveLoadManager.Save() called to persist all level data
  3. Scene unloads
  4. New scene loads
  5. After scene loads: LifecycleManager.BroadcastRestoreRequested(sceneName)
    • All ManagedBehaviours in that scene get OnRestoreRequested() called
    • Each component restores its level-specific data from SaveLoadManager

End of Document