220 lines
9.9 KiB
Markdown
220 lines
9.9 KiB
Markdown
# 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` |