add blueprints
This commit is contained in:
216
docs/blueprints/05-saveload/35_SS_SaveManager.md
Normal file
216
docs/blueprints/05-saveload/35_SS_SaveManager.md
Normal file
@@ -0,0 +1,216 @@
|
||||
# SS_SaveManager — GameInstance Subsystem
|
||||
|
||||
**File:** [`Content/Framework/Save/SS_SaveManager`](Content/Framework/Save/SS_SaveManager.uasset)
|
||||
|
||||
**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`
|
||||
Reference in New Issue
Block a user