// Copyright Epic Games, Inc. All Rights Reserved. // UE5 Modular Game Framework — SS_SaveManager (35) // Save/Load subsystem. Slot management, serialization, manifest tracking. // In C++, uses FArchive for direct binary serialization — far more powerful // than Blueprint "Save Game" / "Load Game" nodes. #pragma once #include "CoreMinimal.h" #include "Subsystems/GameInstanceSubsystem.h" #include "GameplayTagContainer.h" #include "SS_SaveManager.generated.h" /** * Save slot metadata returned by GetSlotManifest(). */ USTRUCT(BlueprintType) struct PG_FRAMEWORK_API FSaveSlotInfo { GENERATED_BODY() UPROPERTY(BlueprintReadOnly) int32 SlotIndex = -1; UPROPERTY(BlueprintReadOnly) FString SlotName; UPROPERTY(BlueprintReadOnly) FString ChapterName; UPROPERTY(BlueprintReadOnly) float PlayTimeHours = 0.0f; UPROPERTY(BlueprintReadOnly) FDateTime Timestamp; UPROPERTY(BlueprintReadOnly) FGameplayTag LastCheckpoint; UPROPERTY(BlueprintReadOnly) bool bIsEmpty = true; }; // Delegates DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnSaveComplete, int32, SlotIndex, bool, bSuccess); DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnLoadComplete, int32, SlotIndex, bool, bSuccess); DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnSaveManifestUpdated, const TArray&, Slots); /** * SS_SaveManager — Save/Load Subsystem. * * Manages all save slots, serialization, and manifest tracking. * C++ gives us direct FArchive-based serialization, proper error handling, * and async save operations — impossible to match in Blueprint. */ UCLASS() class PG_FRAMEWORK_API USS_SaveManager : public UGameInstanceSubsystem { GENERATED_BODY() public: USS_SaveManager(); // ======================================================================== // Lifecycle // ======================================================================== virtual void Initialize(FSubsystemCollectionBase& Collection) override; virtual void Deinitialize() override; // ======================================================================== // Configuration // ======================================================================== /** Maximum number of save slots. */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Framework|Save") int32 MaxSlots = 10; /** Save game file prefix for slot naming. */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Framework|Save") FString SavePrefix = TEXT("FrameworkSave_"); // ======================================================================== // Slot Manifest // ======================================================================== /** * Returns metadata for all save slots. * Fast — reads header only, not full save data. */ UFUNCTION(BlueprintCallable, Category = "Framework|Save") TArray GetSlotManifest() const; /** * Checks if a slot has save data. */ UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Framework|Save") bool DoesSlotExist(int32 SlotIndex) const; // ======================================================================== // Save / Load Operations // ======================================================================== /** * Save game to a slot. Returns true if successful. * Server-authoritative in multiplayer. */ UFUNCTION(BlueprintCallable, Category = "Framework|Save") bool SaveGame(int32 SlotIndex, const FString& Description); /** * Load game from a slot. Returns true if successful. */ UFUNCTION(BlueprintCallable, Category = "Framework|Save") bool LoadGame(int32 SlotIndex); /** * Delete a save slot. Irreversible! */ UFUNCTION(BlueprintCallable, Category = "Framework|Save") bool DeleteSlot(int32 SlotIndex); /** * Quick-save to the current active slot. */ UFUNCTION(BlueprintCallable, Category = "Framework|Save") bool QuickSave(); /** * Quick-load from the current active slot. */ UFUNCTION(BlueprintCallable, Category = "Framework|Save") bool QuickLoad(); // ======================================================================== // Checkpoint Management // ======================================================================== /** Loads the most recent checkpoint from a slot. */ UFUNCTION(BlueprintCallable, Category = "Framework|Save") bool LoadCheckpoint(int32 SlotIndex); /** * Creates a checkpoint within the current slot. * Checkpoints are incremental saves within a single slot. */ UFUNCTION(BlueprintCallable, Category = "Framework|Save") bool CreateCheckpoint(FGameplayTag CheckpointTag); // ======================================================================== // Event Dispatchers // ======================================================================== UPROPERTY(BlueprintAssignable, Category = "Framework|Events") FOnSaveComplete OnSaveComplete; UPROPERTY(BlueprintAssignable, Category = "Framework|Events") FOnLoadComplete OnLoadComplete; UPROPERTY(BlueprintAssignable, Category = "Framework|Events") FOnSaveManifestUpdated OnSaveManifestUpdated; // ======================================================================== // Utilities // ======================================================================== /** Returns the total disk space used by all saves (in bytes). */ UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Framework|Save") int64 GetTotalSaveSize() const; /** Backs up all save slots to a Backup/ subdirectory. */ UFUNCTION(BlueprintCallable, Category = "Framework|Save") bool BackupAllSaves(const FString& BackupLabel); protected: /** Builds the save slot name from prefix + index. */ FString GetSlotName(int32 SlotIndex) const; /** Reads only the header/metadata from a save file. */ FSaveSlotInfo ReadSlotHeader(int32 SlotIndex) const; /** Internal save implementation using FArchive serialization. */ bool SaveToFile(int32 SlotIndex, const TArray& Data, const FSaveSlotInfo& Meta); /** Internal load implementation. */ bool LoadFromFile(int32 SlotIndex, TArray& OutData, FSaveSlotInfo& OutMeta); /** Path to the save directory. */ FString GetSaveDirectory() const; /** Currently active save slot (from GI_GameFramework). */ int32 GetActiveSlot() const; };