Skip to content

Schema Evolution & Migrations ​

As your game evolves across updates, DLCs, and balance patches, your actor classes and variable structures will change. If a player updates their game, their existing save files from Version 1.0 must load cleanly into Version 1.1.

Aegis Save provides a dedicated Schema Migration Framework (UAegisSchemaMigration) to handle property renames, structural shifts, and data transformations automatically.


1. Schema Versions & Metadata ​

Every save file stores a GameSchemaVersion in its metadata header:

cpp
FAegisSaveOptions Options;
Options.GameSchemaVersion = 2; // Incremented for Patch 1.1

UAegisSubsystem::Get(this)->SaveGame(TEXT("Slot01"), Options);

When LoadGame is called:

  1. Aegis reads the file's GameSchemaVersion.
  2. If the file's schema version matches the current game version, properties load directly.
  3. If the file is from an older schema version, Aegis automatically executes the registered migration chain (FromVersion $\rightarrow$ ToVersion) before restoring actors.

2. Property Redirectors (Track A) ​

If you simply renamed a variable (e.g. Health $\rightarrow$ CurrentHealth) or moved a property to a different component, you do not need to write C++ migration code.

Use FAegisPropertyRedirect:

cpp
#include "Schema/AegisSchemaMigration.h"

UCLASS()
class UMyGameMigration_v1_to_v2 : public UAegisSchemaMigration
{
    GENERATED_BODY()

public:
    UMyGameMigration_v1_to_v2()
    {
        FromVersion = 1;
        ToVersion = 2;

        // Redirect Old Property Name -> New Property Name
        FAegisPropertyRedirect HealthRedirect;
        HealthRedirect.TargetClassName = TEXT("BP_PlayerCharacter_C");
        HealthRedirect.OldPropertyName = TEXT("Health");
        HealthRedirect.NewPropertyName = TEXT("CurrentHealth");
        PropertyRedirects.Add(HealthRedirect);
    }
};

When loading a Version 1 save, Aegis automatically maps the stored Health tagged property bytes into the new CurrentHealth variable.


3. Custom Data Transformations (Track B) ​

If you made a structural change—such as converting an integer Gold into an itemized currency struct, or splitting one variable into two:

Override ApplyMigration in your UAegisSchemaMigration subclass:

cpp
bool UMyGameMigration_v1_to_v2::ApplyMigration_Implementation(FAegisMigrationContext& Context)
{
    // Query records in the migration context:
    for (FAegisRecordMigrationEntry& Entry : Context.Records)
    {
        if (Entry.TargetClassName == TEXT("BP_PlayerCharacter_C"))
        {
            // Read old raw property value
            int32 OldGold = 0;
            if (Entry.GetPropertyValue(TEXT("Gold"), OldGold))
            {
                // Create new structured currency entry
                FCurrencyData NewCurrency;
                NewCurrency.GoldCoins = OldGold;
                NewCurrency.SilverCoins = 0;

                Entry.SetPropertyValue(TEXT("PlayerCurrency"), NewCurrency);
                Entry.RemoveProperty(TEXT("Gold"));
            }
        }
    }

    return true; // Return true to commit migration
}

4. Atomic Abort on Failure (C-32) ​

Aegis Save guarantees Atomic Migration Safety:

  • If a migration step encounters an unexpected failure and returns false, the load is immediately aborted with result code EAegisResult::MigrationFailed.
  • The partially migrated save is never written back to disk.
  • The player's existing save file and current session remain 100% untouched and uncorrupted (AC-509 Verified).

Next Steps ​

Aegis Save — Enterprise World Persistence for Unreal Engine 5.