Skip to content

Latest commit

 

History

4 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 

Repository files navigation

VDSaveSystem — Comprehensive User & Developer Guide (UE 5.5)

VDSaveSystem is a high-performance, modular save & load framework for Unreal Engine 5.5, architected around UGameInstanceSubsystem.

Designed for everything from small indie projects to large-scale AAA open-world titles (World Partition, Level Streaming, Data Layers), complex physics simulation, multiplayer, encryption, hardware-accelerated compression, and cloud backend integration (Steam Cloud, Epic Online Services, custom REST APIs).


📑 Table of Contents

  1. Architecture & Key Features
  2. Directory & Save File Structure
  3. Quick Start
  4. Subsystem: UVDSaveManagerSubsystem
  5. World Saving & Loading
  6. Slots, Rotation & AutoSaves
  7. Sidecar Metadata & Screenshots (Thumbnails)
  8. Custom Save Objects (VDCustomSaveGame)
  9. Global Player Profile (Player Profile)
  10. Multiplayer & Network Support
  11. World Partition, Level Streaming & Data Layers
  12. Destroyed Actor Tracking
  13. Save Guards & Blockers
  14. Compression, Encryption & Security
  15. Automated Backups & Storage Quotas
  16. Cloud Saves (Steam Cloud / Epic Online Services)
  17. Screen Transitions (Camera Fade Overlay)
  18. Asset & Class Redirectors
  19. Developer Console Commands
  20. Automation Unit Tests
  21. Project Settings Reference
  22. C++ & Blueprint Integration Examples

1. Architecture & Key Features

  • Subsystem-First Architecture (UGameInstanceSubsystem):
    • Instantiated automatically with UGameInstance and globally accessible from any C++ or Blueprint scope.
    • Zero requirement to place manager actors (SaveManagerActor) on levels.
    • Keeps persistent memory alive across seamless map travel and level transitions.
  • Sidecar Slot File Structure:
    • Each slot is stored in its own isolated subfolder.
    • Slot metadata is duplicated into a lightweight JSON sidecar file (Metadata.json).
    • Loading screens and UI menus can parse 50+ save slots in 1–2 milliseconds without decompressing multi-megabyte binary dumps.
  • World Partition & Data Layer Compatibility:
    • Automatically captures and restores active Data Layer runtime states (EDataLayerRuntimeState::Activated).
    • Seamlessly handles actor state caching when streaming levels/cells unload.
  • Deep Component & Attachment Hierarchy Serialization:
    • Captures dynamic runtime-spawned components (Runtime Components).
    • Preserves nested attached actors (Attached Actors), socket names, and relative transforms.
  • Physics & Velocity Preservation:
    • Stores linear and angular velocities (LinearVelocity, AngularVelocity) for physics-simulating primitives and characters with CharacterMovementComponent.
  • Fault Tolerance & Reliability:
    • Automatic timestamped backups prior to slot overwrite.
    • Header integrity validation with unique magic bytes (VD_SAVE_MAGIC = 0x56445356).
    • Automatic disk storage quota enforcement and backup pruning.

2. Directory & Save File Structure

All save files are stored in [ProjectDirectory]/Saved/VDSaves/ by default:

Saved/
└── VDSaves/
    ├── Slot_1/
    │   ├── SaveData.vdsave          <-- Binary world payload (Oodle compressed + AES-256 encrypted)
    │   ├── SaveData_Backup_*.vdsave  <-- Automatic backup files
    │   ├── Metadata.json            <-- Lightweight JSON metadata for instant UI queries
    │   └── Thumbnail.jpg            <-- Slot screenshot (256x256)
    ├── AutoSave_1/
    │   ├── SaveData.vdsave
    │   ├── Metadata.json
    │   └── Thumbnail.jpg
    ├── QuickSave_1/
    │   ├── SaveData.vdsave
    │   ├── Metadata.json
    │   └── Thumbnail.jpg
    ├── CustomGlobals/               <-- Global custom save objects
    │   └── InventorySave.vdsave
    └── GlobalProfile.vdprof         <-- Global player settings profile (audio, video, input)

3. Quick Start

Property Tagging (SaveGame Flag)

To ensure a property on an Actor or Component is serialized by the save system, mark it with the SaveGame specifier.

In C++:

UPROPERTY(EditAnywhere, BlueprintReadWrite, SaveGame, Category = "Stats")
int32 Health = 100;

UPROPERTY(EditAnywhere, BlueprintReadWrite, SaveGame, Category = "Stats")
TArray<FString> InventoryItems;

In Blueprints:

  1. Select the variable in the My Blueprint panel.
  2. In the Details panel, expand the advanced options.
  3. Check the Save Game checkbox.

Interface: IVDSaveableInterface

Any AActor, UActorComponent, or UObject implementing UVDSaveableInterface participates in the save/load pipeline.

Interface Methods:

Method Description Default Implementation
OnBeforeSave() Called immediately before the object is serialized into the byte buffer. Use this to prepare data. Empty
OnAfterLoad() Called immediately after the object has been deserialized. Use this to update UI, visuals, materials, animations. Empty
ShouldSaveTransform() Determines whether the Actor's world transform (location, rotation, scale) should be saved and restored. true
GetSaveableSubobjects(OutObjects) Allows passing child UObject* instances (e.g. inventory objects, quest trackers) that should also be serialized. Empty Array

C++ Implementation Example:

// MyChestActor.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "VDSaveableInterface.h"
#include "MyChestActor.generated.h"

UCLASS()
class MYGAME_API AMyChestActor : public AActor, public IVDSaveableInterface
{
    GENERATED_BODY()

public:
    UPROPERTY(SaveGame)
    bool bIsOpen = false;

    UPROPERTY(SaveGame)
    TArray<FString> StoredLoot;

    // IVDSaveableInterface
    virtual void OnBeforeSave_Implementation() override;
    virtual void OnAfterLoad_Implementation() override;
    virtual bool ShouldSaveTransform_Implementation() override { return false; } // Static chest
};

// MyChestActor.cpp
#include "MyChestActor.h"

void AMyChestActor::OnBeforeSave_Implementation()
{
    // Prepare data prior to serialization
}

void AMyChestActor::OnAfterLoad_Implementation()
{
    // Apply loaded state to visual representation
    if (bIsOpen)
    {
        // Snap chest lid open without replaying opening animation
    }
}

Actor Identification: UVDSaveIdentityComponent

For dynamically spawned actors (SpawnActor) or actors placed in World Partition levels, attach the UVDSaveIdentityComponent.

  • Automatically generates a stable unique GUID (FGuid StableId).
  • During save/load, the system first matches actors by their StableId. If not found, it falls back to FName.
  • If an actor was spawned during gameplay and does not exist on level load, the system automatically respawns it using the recorded class path (ActorClassPath) and applies the serialized payload.
// Adding in C++ constructor
MyIdentityComp = CreateDefaultSubobject<UVDSaveIdentityComponent>(TEXT("SaveIdentity"));

4. Subsystem: UVDSaveManagerSubsystem

Accessing the Subsystem

In Blueprints:

Use the Get VDSaveManagerSubsystem node or any high-level functions under the VDSaveSystem category.

In C++:

#include "VDSaveManagerSubsystem.h"

// From any UObject with a valid World Context (Actor, Widget, GameMode, etc.):
UVDSaveManagerSubsystem* SaveManager = GetGameInstance()->GetSubsystem<UVDSaveManagerSubsystem>();
if (SaveManager)
{
    SaveManager->SaveGame(this, TEXT("Slot_1"));
}

Lifecycle Delegates & Events

The subsystem provides Blueprint dynamic multicast delegates and native C++ multicast delegates:

Blueprint Delegate Native C++ Delegate Signature Description
OnSaveCompleted OnSaveCompletedNative (bool bSuccess, const FString& SlotName) Fires after save data has been successfully written to disk.
OnLoadCompleted OnLoadCompletedNative (bool bSuccess, const FString& SlotName) Fires after world data has been completely loaded and applied.
OnAutoSaveTriggered (const FString& SlotName) Fires when an automatic save is initiated.
OnProfileSaved (bool bSuccess) Fires after the global profile has been saved.
OnProfileLoaded (bool bSuccess) Fires after the global profile has been loaded.

C++ Delegate Binding Example:

void AMyGameMode::BeginPlay()
{
    Super::BeginPlay();

    if (UVDSaveManagerSubsystem* SaveMgr = GetGameInstance()->GetSubsystem<UVDSaveManagerSubsystem>())
    {
        SaveMgr->OnSaveCompletedNative.AddUObject(this, &AMyGameMode::HandleSaveCompleted);
        SaveMgr->OnLoadCompletedNative.AddUObject(this, &AMyGameMode::HandleLoadCompleted);
    }
}

void AMyGameMode::HandleSaveCompleted(bool bSuccess, const FString& SlotName)
{
    UE_LOG(LogTemp, Log, TEXT("Slot %s save finished with status: %s"), *SlotName, bSuccess ? TEXT("Success") : TEXT("Failure"));
}

5. World Saving & Loading

Synchronous & Asynchronous Execution

The framework provides multiple execution models to suit any game flow:

  1. Background ThreadPool Execution:
    • Object state snapshotting takes place on the GameThread, while binary serialization, Oodle compression, AES encryption, and file I/O execute asynchronously in EAsyncExecution::ThreadPool. Zero frame drops or game hitches.
  2. Blueprint Async Action Nodes:
    • AsyncSaveGame — Saves the specified slot with OnSuccess and OnFailure execution pins.
    • AsyncLoadGame — Loads the specified slot with OnSuccess and OnFailure execution pins.
    • AsyncAutoLoadGame — Finds the latest save slot by timestamp and loads it asynchronously.
  3. Latent Nodes:
    • SaveGameActorsAsync, LoadGameActorsAsync with OnCompleted and OnFailed execution branches.

Data Filters (EVDSaveDataFilter)

The bitmask enum EVDSaveDataFilter enables precise control over which segments of the world are captured or restored:

UENUM(BlueprintType, meta = (Bitflags, UseEnumValuesAsMaskValuesInEditor = "true"))
enum class EVDSaveDataFilter : uint8
{
    None        = 0,
    Player      = 1 << 0,  // Pawn, PlayerController, PlayerState
    LevelActors = 1 << 1,  // All level actors implementing IVDSaveableInterface
    GameState   = 1 << 2,  // GameState
    All         = 0xFF     // Complete snapshot of the game world
};

Use case: To reload only the player character at a checkpoint while leaving environmental destruction intact, supply EVDSaveDataFilter::Player.


Map Transition Loading (LoadGameAndMap)

The LoadGameAndMap(WorldContextObject, SlotName) function performs an automated two-stage transition:

  1. Reads slot metadata to identify the saved level (MapName).
  2. If the current world map differs from the saved map, it loads the destination map via UGameplayStatics::OpenLevel.
  3. Once map loading completes, it automatically restores the world state.

Deferred Time-Sliced Loading

For expansive levels containing thousands of actors, restoring every object in a single frame can cause noticeable hitching.

The plugin provides a Deferred Loading mode:

  • LevelLoadMethod = Deferred
  • LoadBatchSize = 30 (number of level actors restored per frame).

The subsystem registers an FTSTicker delegate to distribute actor deserialization smoothly across multiple frames without dropping below target frame rates.


6. Slots, Rotation & AutoSaves

AutoSave & QuickSave Rotation

  • SaveGameAuto(WorldContextObject) — Saves into rotating slots: AutoSave_1, AutoSave_2, ..., AutoSave_N.
  • SaveGameQuick(WorldContextObject) — Saves into rotating slots: QuickSave_1, ..., QuickSave_N.

Rotation Algorithm:

  1. Checks for unused slots numbered from 1 to MaxAutosaveSlots.
  2. If an unused candidate is available, that slot is used.
  3. If all slots are occupied, the subsystem selects and overwrites the oldest slot based on UTC timestamp.

Periodic Background AutoSave

The framework features an automated timer-based save loop:

  • StartPeriodicAutoSave(IntervalSeconds) — Starts the periodic autosave timer (default: 300 seconds).
  • StopPeriodicAutoSave() — Clears and stops the timer.
  • IsPeriodicAutoSaveActive() — Checks whether the autosave timer is currently running.

Slot Management & Listing

  • GetAllSaveSlots() — Returns TArray<FVDSaveMetadata> sorted by timestamp descending (newest saves first).
  • GetLatestSaveSlotName() — Returns the identifier of the most recent save slot.
  • DeleteSaveSlot(SlotName) — Deletes the slot directory and all associated files (SaveData.vdsave, Metadata.json, Thumbnail.jpg, backups).
  • GetCurrentSaveSlot() / SetCurrentSaveSlot(SlotName) — Gets or sets the active slot name.

7. Sidecar Metadata & Screenshots (Thumbnails)

FVDSaveMetadata Structure

USTRUCT(BlueprintType)
struct FVDSaveMetadata
{
    GENERATED_BODY()

    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    FString SlotName;             // Save slot name

    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    FDateTime Timestamp;          // Creation date/time in UTC

    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    FString MapName;              // Name of the saved level/map

    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    int32 SaveVersion;            // Version code of save schema

    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    float PlaytimeSeconds;        // Cumulative session playtime in seconds

    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    FString ScreenshotPath;       // Relative path to slot thumbnail

    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    TMap<FString, FString> CustomData; // Custom key-value string pairs

    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    FString CharacterName;        // Player character name

    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    int32 PlayerLevel;            // Player character level

    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    FString CurrentQuest;         // Active quest title
};

Zero-Stutter UI Generation

Building a "Load Game" menu requires only a single call:

TArray<FVDSaveMetadata> AllSlots = SaveSubsystem->GetAllSaveSlots();

The subsystem reads only the small Metadata.json files. This operation completes in fractions of a millisecond even on mobile devices and consoles.

Setting Custom Metadata Fields:

Before invoking SaveGame, enrich the metadata for UI presentation:

SaveSubsystem->SetMetadataPlayerInfo(TEXT("Geralt"), 42, TEXT("The Witcher's Calling"));
SaveSubsystem->SetMetadataCustomData(TEXT("Difficulty"), TEXT("DeathMarch"));
SaveSubsystem->SetMetadataCustomData(TEXT("LocationTitle"), TEXT("Novigrad"));

Asynchronous Thumbnail Loading

To prevent frame drops when scrolling through save slots with image previews:

  • Blueprint: The Load Slot Thumbnail Async node loads the screenshot in the background and returns a UTexture2D*.
  • C++: UVDSaveManagerSubsystem::LoadSaveThumbnail(ScreenshotPath).

8. Custom Save Objects (VDCustomSaveGame)

For modular persistence subsystems (e.g. global achievements, talent trees, inventory records, merchant states), the framework provides UVDCustomSaveGame (analogous to EMSCustomSaveGame).

Creating a Custom Save Object

  1. Derive a new class from UVDCustomSaveGame in C++ or Blueprint (e.g. UInventorySaveGame).
  2. Add variables tagged with the SaveGame flag.
  3. Configure the default class properties:
    • SaveFileName — Target file name (e.g. InventoryData).
    • bUseSaveSlot — If true, saves inside the active slot folder; if false, saves globally under Saved/VDSaves/CustomGlobals/.
// InventorySaveGame.h
#pragma once
#include "VDCustomSaveGame.h"
#include "InventorySaveGame.generated.h"

UCLASS()
class MYGAME_API UInventorySaveGame : public UVDCustomSaveGame
{
    GENERATED_BODY()

public:
    UInventorySaveGame()
    {
        SaveFileName = TEXT("InventoryData");
        bUseSaveSlot = true; // Bound to active slot
    }

    UPROPERTY(SaveGame, BlueprintReadWrite)
    TArray<FString> ItemIds;

    UPROPERTY(SaveGame, BlueprintReadWrite)
    int32 Gold = 500;
};

Custom Save API:

// 1. Retrieve or load instance (cached in memory)
UInventorySaveGame* InvSave = Cast<UInventorySaveGame>(SaveSubsystem->GetCustomSaveObject(UInventorySaveGame::StaticClass(), CurrentSlot));

// 2. Modify properties
InvSave->Gold += 100;

// 3. Commit to disk
SaveSubsystem->SaveCustomObject(InvSave, CurrentSlot);

// 4. Check existence on disk
bool bExists = SaveSubsystem->DoesCustomSaveExist(UInventorySaveGame::StaticClass(), CurrentSlot);

// 5. Delete from disk
SaveSubsystem->DeleteCustomSaveObject(UInventorySaveGame::StaticClass(), CurrentSlot);

// 6. Save all open custom objects at once
SaveSubsystem->SaveAllCustomObjects();

Saving Raw Object Collections

To persist arbitrary arrays of UObject* instances without creating dedicated classes:

  • SaveObjectCollection(Objects, SaveSlot, FileName)
  • LoadObjectCollection(Objects, SaveSlot, FileName)

Using the FVDRawObjectSaveData structure:

FVDRawObjectSaveData Item;
Item.DataId = FName("QuestManager");
Item.Object = MyQuestManagerInstance;

9. Global Player Profile (Player Profile)

The global profile is stored in Saved/VDSaves/GlobalProfile.vdprof independently of gameplay save slots. Ideal for:

  • Video, audio, graphics, and keybinding settings.
  • Account-wide statistics, unlockables, and achievements.

Player Profile API:

// Store custom key-value pairs
SaveSubsystem->SetProfileCustomData(TEXT("Audio_MasterVolume"), TEXT("0.85"));
SaveSubsystem->SetProfileCustomData(TEXT("FOV"), TEXT("105"));

// Retrieve values
FString Vol = SaveSubsystem->GetProfileCustomData(TEXT("Audio_MasterVolume"));

// Serialize an entire custom UObject into the profile
SaveSubsystem->SaveProfile(MyUserSettingsObject);
SaveSubsystem->LoadProfile(MyUserSettingsObject);

// Check if a profile file exists on disk
bool bHasProfile = SaveSubsystem->DoesProfileSaveExist();

10. Multiplayer & Network Support

Server Authority & World State

  • World state serialization (Level Actors, Data Layers, GameState) must be executed exclusively on the server (HasSaveAuthority() == true — Dedicated Server, Listen Server, or Standalone).
  • If a client invokes SaveGame with the LevelActors flag, the subsystem safely clamps the filter to EVDSaveDataFilter::Player only.

Individual Player Persistence (SavePlayerState / CustomPlayer)

In multiplayer sessions, connected players are identified by their unique network identifier FUniqueNetIdRepl (SteamID, EOS ProductUserId, etc.).

// Save state for a specific connected PlayerController (Pawn + Controller + PlayerState)
SaveSubsystem->SavePlayerState(PlayerController, SlotName);

// Restore player state (e.g. inside GameMode::OnPostLogin)
SaveSubsystem->LoadPlayerState(PlayerController, SlotName);

Dedicated standalone player file persistence:

  • SaveCustomPlayer(Controller, UniquePlayerId, SaveSlot)
  • LoadCustomPlayer(Controller, UniquePlayerId, SaveSlot)
  • DeleteCustomPlayer(UniquePlayerId, SaveSlot)

11. World Partition, Level Streaming & Data Layers

Data Layers

When bSaveWorldPartitionDataLayers is enabled, the subsystem queries UDataLayerManager, records all active Data Layers, and restores their runtime state (EDataLayerRuntimeState::Activated) upon loading.

Streaming Memory Cache

In games using Level Streaming or World Partition, actors are dynamically unloaded as the player moves across the world.

  • With bEnableStreamingAutoMemoryCache enabled, the subsystem intercepts level unloads (HandleLevelRemovedFromWorld) and caches modified actors into an in-memory map UnloadedLevelActorsCache.
  • When the player returns to that area (HandleLevelAddedToWorld), actors are restored from memory.
  • During a full world save, memory-cached actors from unloaded cells are merged with active scene actors.

Player Physics Protection on Cell Streaming

The bProtectPlayerPhysicsOnCellStream option briefly suspends character physics and gravity during initial cell streaming, preventing the pawn from falling through geometry before collision meshes are loaded.


12. Destroyed Actor Tracking

When an actor on a level is permanently destroyed (e.g. collected key, broken bridge, defeated unique boss), its destruction must be recorded.

Use the destruction tracking helpers:

// 1. Mark actor as destroyed and immediately invoke Destroy()
SaveSubsystem->DestroyAndMarkSaveActor(KeyActor);

// 2. Or register destruction manually before calling standard Destroy()
SaveSubsystem->MarkActorAsDestroyed(KeyActor);
KeyActor->Destroy();

Upon subsequent loads, the subsystem matches recorded actors by StableId or FName and removes them prior to game initialization.


13. Save Guards & Blockers

To prevent saves during sensitive gameplay sequences (cinematic cutscenes, dialogues, execution animations, boss encounters), use the reason-based guard system:

// Block saving with a named reason
SaveSubsystem->AddSaveBlockReason(FName("Cutscene_Intro"));
SaveSubsystem->AddSaveBlockReason(FName("BossFight"));

// Query if saving is allowed
TArray<FName> ActiveReasons;
if (!SaveSubsystem->CanSaveGame(this, ActiveReasons))
{
    // Saving is blocked! ActiveReasons contains the list of blockers
}

// Remove a specific blocker
SaveSubsystem->RemoveSaveBlockReason(this, FName("Cutscene_Intro"));

// Clear all active save blockers
SaveSubsystem->ClearAllSaveBlockReasons();

14. Compression, Encryption & Security

Hardware Compression (Oodle / Zlib)

  • Oodle compression (standard in Unreal Engine 5) is enabled by default, reducing save file sizes on disk by 70–90%.
  • Corrupted or truncated data streams are handled gracefully without application crashes.

Symmetric AES-256 Encryption

To safeguard save files against client-side tampering:

  1. Set bEnableSaveEncryption = true in Project Settings.
  2. Click Generate New Key in the editor or provide a passphrase string.
  3. The passphrase is hashed via SHA-1 to produce a 256-bit AES cipher key for payload encryption.

15. Automated Backups & Storage Quotas

  • bAutoBackupPreviousData — Prior to overwriting an existing slot, the previous file is copied to SaveData_Backup_[Timestamp].vdsave.
  • MaxBackupFilesPerSlot — Maximum number of backup files retained per slot (oldest backups pruned automatically).
  • MaxStorageQuotaMB — Global disk quota for the entire VDSaves/ folder. EnforceStorageQuotaAndCleanupBackups verifies total storage consumption and prunes historical backups when exceeding limits.

16. Cloud Saves (Steam Cloud / Epic Online Services)

For synchronizing slots with Steam Cloud, Epic Online Services (EOS), or cloud backends, the subsystem packs the complete slot folder into a single byte archive:

Single Archive Buffer Packing:

// Export complete slot folder (SaveData.vdsave + Metadata.json + Thumbnail.jpg) to TArray<uint8>
TArray<uint8> CloudArchiveBytes;
if (SaveSubsystem->ExportSlotAsSingleArchiveBuffer(TEXT("Slot_1"), CloudArchiveBytes))
{
    // Send CloudArchiveBytes to Steam Remote Storage / EOS / Cloud REST API
}

// Import from byte buffer back onto local disk
TArray<uint8> DownloadedBytes = /* received from cloud */;
SaveSubsystem->ImportSlotFromSingleArchiveBuffer(TEXT("Slot_1"), DownloadedBytes);

Individual File Cloud API:

  • ExportCloudData(SlotName, FileType, OutBytes, CustomFileName)
  • ImportCloudData(SlotName, FileType, InBytes, CustomFileName)
    • Supported EVDCloudFileType: CompleteSlot, CustomSave, CustomPlayer.

17. Screen Transitions (Camera Fade Overlay)

To conceal level streaming and actor repositioning hitches during loads, the plugin includes a native Slate overlay widget SVDFadeOverlay:

  • In Project Settings:
    • bEnableCameraFadeOnLoad = true
    • CameraFadeDuration = 0.5 seconds.
    • CameraFadeColor = FLinearColor::Black
  • When loading begins, the viewport fades to black and smoothly fades back in once all level actors have been initialized.

18. Asset & Class Redirectors

When renaming Blueprint classes or reorganizing folder structures during game development, legacy saves remain compatible:

  • RuntimeActorRedirectsTMap<FSoftClassPath, FSoftClassPath> for remapping moved actor classes.
  • LevelRedirectsTMap<FSoftObjectPath, FSoftObjectPath> for remapping renamed map assets.

19. Developer Console Commands

Access these commands in the developer console (~):

Command Example Description
vd.save [SlotName] vd.save Slot_Debug Saves the world to the specified slot (defaults to ManualSave_Console).
vd.load [SlotName] vd.load Slot_Debug Loads the specified slot. If omitted, loads the latest save slot.
vd.list vd.list Outputs a formatted table of all save slots (playtime, map, level, timestamp).
vd.wipe vd.wipe Warning: Recursively deletes the entire Saved/VDSaves/ folder.

20. Automation Unit Tests

The plugin includes 5 automated tests accessible via Tools -> Session Frontend -> Automation:

  1. VDSaveSystem.Crypto.AES256 — Validates encryption and decryption integrity with correct and invalid keys.
  2. VDSaveSystem.Compression.OodleVsZlib — Validates Oodle compression and decompression ratios.
  3. VDSaveSystem.Serialization.PhysicsAndTransforms — Tests physics velocity, transform, and component serialization.
  4. VDSaveSystem.Storage.QuotaAndBackups — Tests disk quota enforcement and automated backup pruning.
  5. VDSaveSystem.Cloud.ArchivePackingAndUnpacking — Validates single-archive packing and unpacking for cloud saves.

21. Project Settings Reference

Located under: Project Settings -> Game -> VD Save System.

Security

Property Type Default Description
bEnableSaveEncryption bool false Enable symmetric AES-256 encryption for save payloads.
SaveEncryptionKey FString "" Passphrase string hashed into a 256-bit encryption key.

Storage

Property Type Default Description
SaveDirectoryName FString "VDSaves" Subfolder inside Saved/ for storing save data.
ProfileSaveFileName FString "GlobalProfile.vdprof" File name for the global player profile.
bUseCompression bool true Enable Oodle / Zlib compression.
bAutoBackupPreviousData bool true Automatically back up slot files before overwriting.
MaxBackupFilesPerSlot int32 2 Maximum backup copies per slot (0 = unlimited).
MaxStorageQuotaMB int32 0 Disk quota limit in megabytes (0 = unlimited).

Loading

Property Type Default Description
LevelLoadMethod EVDLoadMethod Immediate Deserialization mode: Immediate (single frame) or Deferred (time-sliced).
LoadBatchSize int32 30 Number of actors restored per frame in deferred mode.

Streaming & World Partition

Property Type Default Description
bEnableStreamingAutoMemoryCache bool true Cache modified actor states in memory when levels/cells unload.
bSaveWorldPartitionDataLayers bool true Automatically capture and restore active Data Layer states.
bProtectPlayerPhysicsOnCellStream bool true Suspend character gravity briefly during cell streaming.

Rotation Limits & AutoSave

Property Type Default Description
MaxAutosaveSlots int32 5 Maximum number of rotating autosave slots (AutoSave_1..N).
MaxQuicksaveSlots int32 3 Maximum number of rotating quicksave slots (QuickSave_1..N).
bEnablePeriodicAutoSave bool false Enable periodic timer-based autosaving at game start.
AutoSaveIntervalSeconds float 300.0 Interval between periodic autosaves in seconds.

Screenshot & Camera Fade

Property Type Default Description
bAutoCaptureScreenshot bool true Capture a scene thumbnail when saving a slot.
ScreenshotResolution FIntPoint (256, 256) Thumbnail resolution.
ScreenshotFormat EVDSaveScreenshotFormat JPG Thumbnail image format (JPG, PNG, EXR).
CompressionQuality int32 85 JPEG compression quality (1-100).
bEnableCameraFadeOnLoad bool true Show full-screen camera fade overlay during world loading.
CameraFadeDuration float 0.5 Fade in/out duration in seconds.
CameraFadeColor FLinearColor Black Solid color for loading overlay.

22. C++ & Blueprint Integration Examples

Example 1: QuickSave & QuickLoad via Input Actions (C++)

// Inside your APlayerController or ACharacter:
void AMyPlayerController::SetupInputComponent()
{
    Super::SetupInputComponent();
    InputComponent->BindAction("QuickSave", IE_Pressed, this, &AMyPlayerController::OnQuickSavePressed);
    InputComponent->BindAction("QuickLoad", IE_Pressed, this, &AMyPlayerController::OnQuickLoadPressed);
}

void AMyPlayerController::OnQuickSavePressed()
{
    if (UVDSaveManagerSubsystem* SaveMgr = GetGameInstance()->GetSubsystem<UVDSaveManagerSubsystem>())
    {
        SaveMgr->SaveGameQuick(this);
    }
}

void AMyPlayerController::OnQuickLoadPressed()
{
    if (UVDSaveManagerSubsystem* SaveMgr = GetGameInstance()->GetSubsystem<UVDSaveManagerSubsystem>())
    {
        SaveMgr->AutoLoadGame(this); // Loads the most recent QuickSave or AutoSave
    }
}

Example 2: Populating a Save Slot Selection Widget (Blueprint)

[Event Construct]
       │
       ▼
[Get VDSaveManagerSubsystem] ──► [Get All Save Slots] ──► [ForEachLoop]
                                                               │
                                  ┌────────────────────────────┘
                                  ▼
                     [Create Widget: WBP_SaveSlotEntry]
                                  │
                                  ├─► Set SlotName (Array Element -> SlotName)
                                  ├─► Set PlaytimeText (Array Element -> PlaytimeSeconds)
                                  ├─► Set MapNameText (Array Element -> MapName)
                                  ├─► Set TimestampText (Array Element -> Timestamp)
                                  │
                                  ├─► [Load Slot Thumbnail Async (SlotName)]
                                  │          │
                                  │          ▼
                                  │   [Set Brush from Texture (Image_Preview)]
                                  │
                                  ▼
                     [ScrollBox_Slots -> Add Child]

Example 3: Subobject Serialization via IVDSaveableInterface (C++)

// Character with custom subobject quest log and talent tree:
void AMyCharacter::GetSaveableSubobjects_Implementation(TArray<UObject*>& OutObjects)
{
    if (QuestLogInstance)
    {
        OutObjects.Add(QuestLogInstance);
    }
    if (SkillTreeInstance)
    {
        OutObjects.Add(SkillTreeInstance);
    }
}

⚖️ License & Distribution (Fab / Unreal Engine Marketplace)

This plugin is a commercial product distributed exclusively on Fab (Epic Games).

  • License: By purchasing this plugin on Fab, you are granted a standard Epic Games Content License to use the product in an unlimited number of commercial and non-commercial projects (games, interactive apps, simulations, cinematic productions).
  • Full Source Code Included: The plugin includes complete C++ source code and full Blueprint integration. You are free to modify, extend, and tailor the source code to the specific architectural requirements of your project.
  • Distribution Restrictions: Direct resale, sublicensing, sharing, or public hosting of the plugin's source code or assets (including public GitHub/GitLab repositories) is strictly prohibited. The plugin may only be distributed as part of packaged, compiled final products (executable game builds).
  • Updates & Engine Support: Your purchase on Fab includes lifetime access to all future updates, enhancements, bug fixes, and compatibility releases for future versions of Unreal Engine (UE 5.5, 5.6, 5.7, 5.8+).

Built with passion for Unreal Engine game developers.

About

Advanced, high-performance, and automated Save/Load system for Unreal Engine 5.5+

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors