# SS_SaveManager — GameInstance Subsystem

**File:** [`Content/Framework/Save/SS_SaveManager`](Content/Framework/Save/SS_SaveManager.uasset)

> **⚡ C++ Status: Full Implementation** — `Source/PG_Framework/Public/Save/SS_SaveManager.h` provides the complete save/load subsystem: slot manifest, save/load/delete, quick-save/load, checkpoint management, backup, FArchive binary serialization. **Auto-created** by UE's subsystem system when `GI_GameFramework` initializes — no BP child, no spawning needed. Access from any BP: `Get Game Instance → Get Subsystem(SS_SaveManager)`. See `docs/developer/cpp-integration-guide.md`.
>
> ---

**Purpose:** The single authority for all serialisation and deserialisation of game state to disk. Supports multiple save slots, checkpoint saves, hard saves, auto-saves, and world object persistence.

**Depends On:** [`GI_GameFramework`](../01-core/04_GI_GameFramework.md), [`I_Persistable`](30_I_Persistable.md)
**Used By:** [`BPC_CheckpointSystem`](31_BPC_CheckpointSystem.md), [`WBP_SaveLoadMenu`](../06-ui/), [`BPC_PlayerRespawnSystem`](31_BPC_CheckpointSystem.md)

---

## Enums

```cpp
// E_SaveType — Categorizes the trigger that initiated a save
E_SaveType
{
  Checkpoint,    // Auto-triggered by crossing a checkpoint volume
  HardSave,      // Player-initiated manual save
  AutoSave,      // Periodic or event-triggered auto save
  DeathStateSave // Snapshot created when the player dies (for death loop continuity)
}
```

---

## Structs

```cpp
// S_SaveSlotInfo — Metadata entry for one save slot in the manifest
S_SaveSlotInfo
{
  SlotIndex:        Integer       // 0-based slot number
  DisplayName:      FText         // Player-chosen or auto-generated name
  Timestamp:        FDateTime     // UTC timestamp of last save
  ChapterTag:       GameplayTag   // Which chapter/level was active
  ThumbnailBytes:   Byte Array    // Compressed screenshot for UI preview
  PlaytimeSeconds:  Float         // Total playtime from GI_GameFramework
}

// S_WorldObjectState — Delta record for one persistent world actor
S_WorldObjectState
{
  ObjectTag:        GameplayTag   // Unique identifier matching the actor's tag
  bDestroyed:       Bool          // Was the actor destroyed?
  CustomData:       Map (Name → String)  // Generic key-value store for any component state
}

// S_PlayerSnapshot — Complete capture of player state at save time
S_PlayerSnapshot
{
  // Core vitals
  Health:             Float       // Current HP
  Stress:             Float       // Current stress level
  Stamina:            Float       // Current stamina pool

  // Transform
  Position:           Vector      // World location
  Rotation:           Rotator     // World rotation

  // Inventory
  InventoryData:      Array of S_InventorySlot  // From BPC_InventorySystem
  QuickSlotData:      Array of S_QuickSlotEntry // From BPC_InventoryQuickSlot
  EquipmentData:      Map (GameplayTag → S_EquipmentSlot)  // From BPC_EquipmentSystem

  // Narrative
  NarrativeFlags:     Map (GameplayTag → Bool)   // From BPC_NarrativeStateSystem
  NarrativeValues:    Map (GameplayTag → Float)

  // Metrics
  DeathCount:         Integer     // From BPC_PlayerMetricsTracker
  ItemsCollected:     Integer
  DistanceTravelled:  Float

  // Additional
  ActiveObjectiveTags:  Array of GameplayTag  // From BPC_ObjectiveSystem
  HidingState:          Bool                  // Was player hiding?
  CurrentHidingSpotTag: GameplayTag
  AltDeathSpaceEnterCount: Integer
}
```

---

## Variables

| Name | Type | Description |
|------|------|-------------|
| `SlotManifest` | Array of `S_SaveSlotInfo` | Cached metadata for all available save slots |
| `ActiveSaveObject` | SaveGame Object Reference | The currently loaded save data object in memory |
| `bIsSaving` | Bool | Prevents concurrent save operations |
| `bIsLoading` | Bool | Prevents concurrent load operations |
| `SaveVersion` | Integer | Current schema version for migration detection |
| `WorldStateDelta` | Map (Name → `S_WorldObjectState`) | Live delta of changed world objects since last save |
| `PersistentActors` | Array of Actor Reference | All registered `I_Persistable` actors in the current level |

---

## Functions / Events

| Name | Inputs | Outputs | Description |
|------|--------|---------|-------------|
| `SaveToSlot` | SlotIndex: Int, SaveType: E_SaveType | Bool Success | Orchestrates full save: collects all state, writes SaveGame to disk, updates manifest |
| `LoadFromSlot` | SlotIndex: Int | Bool Success | Reads SaveGame from disk, migrates if needed, distributes state to all systems |
| `DeleteSlot` | SlotIndex: Int | — | Removes save file and manifest entry |
| `GetSlotManifest` | — | Array of `S_SaveSlotInfo` | Returns all slot metadata for UI display |
| `GetSaveFileSize` | SlotIndex: Int | Int64 Bytes | Returns the on-disk file size for the given slot |
| `RegisterPersistableActor` | Actor: Actor Reference | — | Adds an `I_Persistable` actor to the tracked list |
| `UnregisterPersistableActor` | Actor: Actor Reference | — | Removes an `I_Persistable` actor from the tracked list |
| `CollectWorldState` | — | — | Iterates all registered `I_Persistable` actors and calls `CollectState()` |
| `DistributeWorldState` | — | — | Iterates all registered `I_Persistable` actors and calls `RestoreState()` |
| `CollectPlayerSnapshot` | — | `S_PlayerSnapshot` | Gathers player state from all relevant components |
| `DistributePlayerSnapshot` | Snapshot: S_PlayerSnapshot | — | Restores player state to all relevant components |
| `MigrateSaveVersion` | OldVersion: Int, Data: SaveGame Object | SaveGame Object | Upgrades old save schema to current version |
| `SaveCheckpoint` | CheckpointTag: GameplayTag | — | Saves checkpoint data to the active slot without prompting the player |
| `SaveDeathState` | — | — | Saves a special death-state snapshot for death loop continuity |
| `LoadDeathState` | — | Bool Success | Loads the death-state snapshot |
| `HasSaveData` | SlotIndex: Int | Bool | Returns whether a save file exists for the slot |
| `GetActiveSaveSizeInfo` | — | FText | Formatted string with playtime, chapter, timestamp for HUD |

---

## Event Dispatchers

| Name | Parameters | Fired When |
|------|-----------|------------|
| `OnSaveComplete` | SlotIndex: Int, SaveType: E_SaveType | Save operation finishes successfully |
| `OnLoadComplete` | SlotIndex: Int | Load operation finishes successfully |
| `OnSaveFailed` | SlotIndex: Int, ErrorCode: Int | Save fails (disk full, write error, etc.) |
| `OnLoadFailed` | SlotIndex: Int, ErrorCode: Int | Load fails (corrupt file, version mismatch, etc.) |
| `OnWorldStateCollected` | ActorCount: Int | After all `I_Persistable` actors collected state |
| `OnWorldStateDistributed` | ActorCount: Int | After all `I_Persistable` actors restored state |
| `OnSaveVersionMigration` | OldVersion: Int, NewVersion: Int | When a save file is migrated to a newer schema |

---

## Blueprint Flow Diagram

```mermaid
flowchart TD
    A[SaveToSlot called] --> B{bIsSaving?}
    B -->|Yes| C[Return False]
    B -->|No| D[Set bIsSaving = true]
    D --> E[CollectPlayerSnapshot]
    E --> F[CollectWorldState]
    F --> G[Write SaveGame to disk]
    G --> H[Update SlotManifest]
    H --> I[Set bIsSaving = false]
    I --> J[Broadcast OnSaveComplete]
    J --> K[Return True]

    L[LoadFromSlot called] --> M{bIsLoading?}
    M -->|Yes| N[Return False]
    M -->|No| O[Set bIsLoading = true]
    O --> P[Read SaveGame from disk]
    P --> Q{File valid?}
    Q -->|No| R[Broadcast OnLoadFailed]
    R --> S[Set bIsLoading = false]
    S --> T[Return False]
    Q -->|Yes| U[Check SaveVersion]
    U --> V{Needs migration?}
    V -->|Yes| W[MigrateSaveVersion]
    W --> X[DistributePlayerSnapshot]
    V -->|No| X
    X --> Y[DistributeWorldState]
    Y --> Z[Set bIsLoading = false]
    Z --> AA[Broadcast OnLoadComplete]
    AA --> AB[Return True]
```

---

## Save File Structure

```
SaveGame Object (USaveGame-derived)
├── Header
│   ├── SaveVersion: Integer
│   └── SlotInfo: S_SaveSlotInfo
├── Player Snapshot
│   └── S_PlayerSnapshot
├── World State
│   └── WorldStateDelta: Map (Name → S_WorldObjectState)
└── Death State
    └── DeathStateData: S_PlayerSnapshot (optional, only in DeathStateSave)
```

---

## Communication Matrix

| Target System | Method | Why |
|---------------|--------|-----|
| All `I_Persistable` actors | Interface call (`CollectState`, `RestoreState`) | World object persistence |
| `BPC_HealthSystem` / Stress / Stamina | Via `S_PlayerSnapshot` | Restore player vitals |
| `BPC_InventorySystem` | Via `S_PlayerSnapshot` | Restore inventory state |
| `BPC_NarrativeStateSystem` | Via `S_PlayerSnapshot` | Restore narrative flags |
| `BPC_PlayerMetricsTracker` | Via `S_PlayerSnapshot` | Restore session metrics |
| `BPC_ObjectiveSystem` | Via `S_PlayerSnapshot` | Restore objective state |
| `BPC_EquipmentSystem` | Via `S_PlayerSnapshot` | Restore equipment state |
| `BPC_InventoryQuickSlot` | Via `S_PlayerSnapshot` | Restore quick slot layout |
| `BPC_CheckpointSystem` | Dispatcher `OnLoadComplete` | Notify checkpoint system to reset |
| `WBP_SaveLoadMenu` | Dispatcher `OnSaveComplete`/`OnLoadComplete` | UI refresh after operation |
| `GI_GameFramework` | Direct (owned by) | Access active slot index, update phase |
| `BPC_DeathHandlingSystem` | Direct call `SaveDeathState`/`LoadDeathState` | Death loop continuity |

---

## Reuse Notes

- Add new fields to `S_PlayerSnapshot` per project without breaking existing saves (migration handles missing fields)
- The `WorldStateDelta` map handles any `I_Persistable` actor generically — no per-actor serialisation code needed
- Save file naming convention: `SaveSlot_{SlotIndex}.sav`
- All disk I/O uses UE5's `UGameplayStatics::SaveGameToSlot` / `LoadGameFromSlot`
- Register/unregister `I_Persistable` actors in `BeginPlay` / `EndPlay` of the actor
- For multiplayer, the `GM_CoreGameMode` should be the Server-authorised caller of `SaveToSlot` / `LoadFromSlot`