Version 1.6.1 | Production Ready | Unreal Engine 5.5+
The Persistent Map is a production-ready actor-based persistence system that manages the saving and loading of actors to/from Supabase with advanced batch processing, error recovery, and monitoring capabilities.
The APersistentMap actor provides enterprise-level persistence functionality for Unreal Engine projects using Supabase as the backend. It offers sophisticated batch operations, intelligent retry logic, and comprehensive monitoring for production environments.
UCLASS(BlueprintType)
class SUPABASE_API APersistentMap : public AActor
The system consists of three main components:
USTRUCT(BlueprintType)
struct FPersistentMapStats
{
int32 TotalActors;
int32 SavedActors;
int32 LoadedActors;
int32 FailedActors;
FDateTime LastOperation;
FString LastOperationType;
bool bOperationInProgress;
};
// Spawn and configure PersistentMap
APersistentMap* PersistentMap = GetWorld()->SpawnActor<APersistentMap>();
PersistentMap->SetConnection(MySupabaseConnection);
PersistentMap->SetTableName(TEXT("my_persistent_actors"));
| Property | Type | Default | Description |
|---|---|---|---|
Connection |
USupabaseConnection* |
nullptr |
Supabase connection reference |
TableName |
FString |
"persistent_actors" |
Database table name |
bAutoSaveEnabled |
bool |
false |
Enable automatic saving |
AutoSaveInterval |
float |
300.0f |
Auto-save interval in seconds |
BatchSize |
int32 |
20 |
Number of actors per batch |
bEnableValidation |
bool |
true |
Enable data validation |
bVerboseLogging |
bool |
false |
Enable detailed logging |
// Save all actors with EntityPersistence components
PersistentMap->SaveActorsToSupabase();
// Load actors from database (clear existing)
PersistentMap->LoadActorsFromSupabase(true);
// Sync operation (load then save)
PersistentMap->SyncActorsWithSupabase();
// Enable auto-save with 5-minute intervals
PersistentMap->EnableAutoSave(true, 300.0f);
// Disable auto-save
PersistentMap->EnableAutoSave(false, 0.0f);
// Save to local JSON file
PersistentMap->SaveActorsToFile("MyActors.json");
// Load from local JSON file
PersistentMap->LoadActorsFromFile("MyActors.json");
// Get current statistics
FPersistentMapStats Stats = PersistentMap->GetStats();
UE_LOG(LogTemp, Log, TEXT("Total Actors: %d, Saved: %d, Failed: %d"),
Stats.TotalActors, Stats.SavedActors, Stats.FailedActors);
// Reset statistics
PersistentMap->ResetStats();
// Manually add an actor to the persistent map
PersistentMap->AddActor(MyActor);
// Remove an actor from the persistent map
PersistentMap->RemoveActor(MyActor);
The UPersistentData class is a UPrimaryDataAsset that allows you to store and manage actor persistence data as a data asset:
UCLASS(BlueprintType)
class SUPABASE_API UPersistentData : public UPrimaryDataAsset
{
GENERATED_BODY()
public:
// Array of persisted actor data
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Persistence")
TArray<FActorData> ActorsData;
// Save actor data to the asset
UFUNCTION(BlueprintCallable, Category = "Persistence")
void SaveActorData(const FActorData& ActorData);
// Get actor data by actor ID
UFUNCTION(BlueprintCallable, Category = "Persistence")
bool GetActorData(const FString& ActorID, FActorData& OutActorData) const;
// Remove actor data by actor ID
UFUNCTION(BlueprintCallable, Category = "Persistence")
bool RemoveActorData(const FString& ActorID);
};
Usage Example:
// Load persistent data asset
UPersistentData* DataAsset = LoadObject<UPersistentData>(nullptr, TEXT("/Game/Data/MyPersistentData"));
// Save an actor's data
FActorData ActorData;
ActorData.ActorID = TEXT("MyActor_001");
ActorData.LevelName = TEXT("MainLevel");
ActorData.Transform = MyActor->GetActorTransform();
DataAsset->SaveActorData(ActorData);
// Retrieve actor data
FActorData RetrievedData;
if (DataAsset->GetActorData(TEXT("MyActor_001"), RetrievedData))
{
// Spawn or update actor from retrieved data
}
The Persistent Map provides comprehensive event handling through Blueprint-assignable delegates:
// Save operation completed
UPROPERTY(BlueprintAssignable)
FOnPersistentMapOperation OnSaveComplete;
// Load operation completed with actor details
UPROPERTY(BlueprintAssignable)
FOnActorsLoaded OnLoadComplete;
// Sync operation completed
UPROPERTY(BlueprintAssignable)
FOnPersistentMapOperation OnSyncComplete;
// Error occurred during operation
UPROPERTY(BlueprintAssignable)
FOnPersistentMapOperation OnError;
UFUNCTION(BlueprintCallable, Category = "Supabase|Persistence")
void SaveActorsToSupabase(bool bForceSync = false);
UFUNCTION(BlueprintCallable, Category = "Supabase|Persistence")
void LoadActorsFromSupabase(bool bClearExisting = true);
UFUNCTION(BlueprintCallable, Category = "Supabase|Persistence")
void SyncActorsWithSupabase();
USTRUCT(BlueprintType)
struct FActorData
{
FString ActorID; // Unique identifier
FTransform Transform; // World transform
TMap<FString, FString> CustomData; // Custom properties
FString LevelName; // Source level
TSoftClassPtr<AActor> ActorClass; // Actor class reference
// Serialize actor data to JSON string
FString Serialize() const
{
TSharedPtr<FJsonObject> JsonObject = MakeShared<FJsonObject>();
JsonObject->SetStringField(TEXT("ActorID"), ActorID);
JsonObject->SetStringField(TEXT("LevelName"), LevelName);
// ... transform and custom data serialization
FString OutputString;
TSharedRef<TJsonWriter<>> Writer = TJsonWriterFactory<>::Create(&OutputString);
FJsonSerializer::Serialize(JsonObject.ToSharedRef(), Writer);
return OutputString;
}
// Deserialize actor data from JSON string
bool Deserialize(const FString& JsonString)
{
TSharedPtr<FJsonObject> JsonObject;
TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(JsonString);
if (FJsonSerializer::Deserialize(Reader, JsonObject))
{
ActorID = JsonObject->GetStringField(TEXT("ActorID"));
LevelName = JsonObject->GetStringField(TEXT("LevelName"));
// ... transform and custom data deserialization
return true;
}
return false;
}
};
The Persistent Map expects a Supabase table with the following structure:
CREATE TABLE persistent_actors (
id SERIAL PRIMARY KEY,
actor_id TEXT UNIQUE NOT NULL,
transform JSONB NOT NULL,
custom_data JSONB,
level_name TEXT,
actor_class TEXT,
saved_at TIMESTAMP DEFAULT NOW(),
created_at TIMESTAMP DEFAULT NOW()
);
#if WITH_EDITOR
UFUNCTION(BlueprintCallable, CallInEditor)
void FetchAllActorsWithEntityPersistence();
UFUNCTION(BlueprintCallable, CallInEditor)
void TestSaveToFile();
UFUNCTION(BlueprintCallable, CallInEditor)
void ValidateAllActors();
#endif
The system provides comprehensive validation:
The Persistent Map works seamlessly with the EntityPersistence component:
// Automatically detects EntityPersistence components
if (UEntityPersistence* EntityPersistence = Actor->FindComponentByClass<UEntityPersistence>())
{
ActorData.CustomData.Add(TEXT("HasEntityPersistence"), TEXT("true"));
}
| Issue | Cause | Solution |
|---|---|---|
| Save operations fail | Invalid connection | Check Supabase connection configuration |
| Actors not loading | Table schema mismatch | Verify database table structure |
| Memory leaks | Timer not cleared | Ensure proper EndPlay cleanup |
| Performance issues | Large batch sizes | Reduce batch size configuration |
Enable verbose logging for detailed operation tracking:
// Enable in constructor or BeginPlay
bVerboseLogging = true;
// Check connection status
bool bIsValid = PersistentMap->ValidateConnection();
UE_LOG(LogTemp, Log, TEXT("Connection Valid: %s"), bIsValid ? TEXT("Yes") : TEXT("No"));
This documentation is part of the Seven Mountains Labs Supabase UE5 Plugin wiki. For the latest updates and community support, visit our documentation portal.