Player Data Storage
Save and load player-specific data like save games, settings, and progress to the cloud
Player Data Storage lets you save player-specific data to the cloud. Data syncs across devices automatically when players log in.
Player Data Storage is per-user. Each player can only access their own data. For shared game data, use Title Storage.
Storage limit is 400MB per player. Files are encrypted and only accessible by the owning player.
Write File
Save data to the cloud. Creates the file if it doesn't exist, or overwrites if it does.

- Filename: Name of the file to write
- File Data: Raw bytes to save
Use the Save Game to Bytes helper to convert a SaveGame object to bytes.
#include "EIKCore/Public/Interfaces/IEIKUserFile.h"
void UMyClass::SavePlayerData(const TArray<uint8>& Data)
{
TSharedPtr<IEIKUserFile> UserFile = GetUserFileInterface();
if (!UserFile.IsValid()) return;
FEIKWriteUserFileParams Params;
Params.Filename = TEXT("savegame.sav");
Params.Contents = Data;
UserFile->WriteFile(0, Params,
FEIKAsyncCallback<void>::CreateLambda(
[](const TEIKAsyncResult<void>& Result)
{
if (Result.IsSuccessful())
{
UE_LOG(LogTemp, Log, TEXT("File saved to cloud"));
}
}));
}#include "Online/OnlineServices.h"
#include "Online/UserFile.h"
using namespace UE::Online;
void UMyClass::SavePlayerData(const TArray<uint8>& Data)
{
IOnlineServicesPtr Services = GetServices(EOnlineServices::Epic);
IUserFilePtr UserFile = Services->GetUserFileInterface();
FUserFileWriteFile::Params Params;
Params.LocalAccountId = GetLocalAccountId();
Params.Filename = TEXT("savegame.sav");
Params.FileContents = MakeShared<TArray<uint8>>(Data);
UserFile->WriteFile(MoveTemp(Params))
.OnComplete([](const TOnlineResult<FUserFileWriteFile>& Result)
{
if (Result.IsOk())
{
UE_LOG(LogTemp, Log, TEXT("File saved to cloud"));
}
});
}#include "OnlineSubsystem.h"
#include "Interfaces/OnlineUserCloudInterface.h"
void UMyClass::SavePlayerData(const TArray<uint8>& Data)
{
IOnlineSubsystem* OnlineSub = IOnlineSubsystem::Get(FName(TEXT("EIK")));
IOnlineUserCloudPtr UserCloud = OnlineSub->GetUserCloudInterface();
FUniqueNetIdPtr UserId = OnlineSub->GetIdentityInterface()->GetUniquePlayerId(0);
TArray<uint8> FileData = Data;
UserCloud->WriteUserFile(*UserId, TEXT("savegame.sav"), FileData);
UserCloud->AddOnWriteUserFileCompleteDelegate_Handle(
FOnWriteUserFileCompleteDelegate::CreateLambda(
[](bool bWasSuccessful, const FUniqueNetId& UserId, const FString& FileName)
{
if (bWasSuccessful)
{
UE_LOG(LogTemp, Log, TEXT("File saved: %s"), *FileName);
}
}));
}Read File
Load data from the cloud.

- Filename: Name of the file to read
- File Data (output): Raw bytes loaded from cloud
Use the Bytes to Save Game helper to convert bytes back to a SaveGame object.
void UMyClass::LoadPlayerData()
{
TSharedPtr<IEIKUserFile> UserFile = GetUserFileInterface();
FEIKReadUserFileParams Params;
Params.Filename = TEXT("savegame.sav");
UserFile->ReadFile(0, Params,
FEIKAsyncCallback<FEIKReadUserFileResult>::CreateLambda(
[this](const TEIKAsyncResult<FEIKReadUserFileResult>& Result)
{
if (Result.IsSuccessful())
{
const TArray<uint8>& Data = Result.GetValue().FileContents.Data;
// Process loaded data
}
}));
}void UMyClass::LoadPlayerData()
{
IUserFilePtr UserFile = GetUserFileInterface();
FUserFileReadFile::Params Params;
Params.LocalAccountId = GetLocalAccountId();
Params.Filename = TEXT("savegame.sav");
UserFile->ReadFile(MoveTemp(Params))
.OnComplete([this](const TOnlineResult<FUserFileReadFile>& Result)
{
if (Result.IsOk())
{
const TArray<uint8>& Data = *Result.GetOkValue().FileContents;
// Process loaded data
}
});
}void UMyClass::LoadPlayerData()
{
IOnlineSubsystem* OnlineSub = IOnlineSubsystem::Get(FName(TEXT("EIK")));
IOnlineUserCloudPtr UserCloud = OnlineSub->GetUserCloudInterface();
FUniqueNetIdPtr UserId = OnlineSub->GetIdentityInterface()->GetUniquePlayerId(0);
UserCloud->ReadUserFile(*UserId, TEXT("savegame.sav"));
UserCloud->AddOnReadUserFileCompleteDelegate_Handle(
FOnReadUserFileCompleteDelegate::CreateLambda(
[this, UserCloud, UserId](bool bWasSuccessful, const FUniqueNetId& Id, const FString& FileName)
{
if (bWasSuccessful)
{
TArray<uint8> FileData;
UserCloud->GetFileContents(*UserId, FileName, FileData);
// Process loaded data
}
}));
}Enumerate Files
List all cloud files for the player.

Returns an array of file metadata including filename, size, and last modified time.
void UMyClass::ListCloudFiles()
{
TSharedPtr<IEIKUserFile> UserFile = GetUserFileInterface();
FEIKEnumerateUserFilesParams Params;
UserFile->EnumerateFiles(0, Params,
FEIKAsyncCallback<FEIKEnumerateUserFilesResult>::CreateLambda(
[](const TEIKAsyncResult<FEIKEnumerateUserFilesResult>& Result)
{
if (Result.IsSuccessful())
{
for (const FEIKUserFileMetadata& File : Result.GetValue().Files)
{
UE_LOG(LogTemp, Log, TEXT("%s (%lld bytes)"),
*File.Filename, File.FileSizeBytes);
}
}
}));
}void UMyClass::ListCloudFiles()
{
IUserFilePtr UserFile = GetUserFileInterface();
FUserFileEnumerateFiles::Params Params;
Params.LocalAccountId = GetLocalAccountId();
UserFile->EnumerateFiles(MoveTemp(Params))
.OnComplete([](const TOnlineResult<FUserFileEnumerateFiles>& Result)
{
if (Result.IsOk())
{
UE_LOG(LogTemp, Log, TEXT("Files enumerated"));
}
});
}void UMyClass::ListCloudFiles()
{
IOnlineSubsystem* OnlineSub = IOnlineSubsystem::Get(FName(TEXT("EIK")));
IOnlineUserCloudPtr UserCloud = OnlineSub->GetUserCloudInterface();
FUniqueNetIdPtr UserId = OnlineSub->GetIdentityInterface()->GetUniquePlayerId(0);
UserCloud->EnumerateUserFiles(*UserId);
UserCloud->AddOnEnumerateUserFilesCompleteDelegate_Handle(
FOnEnumerateUserFilesCompleteDelegate::CreateLambda(
[UserCloud, UserId](bool bWasSuccessful, const FUniqueNetId& Id)
{
if (bWasSuccessful)
{
TArray<FCloudFileHeader> Files;
UserCloud->GetUserFileList(*UserId, Files);
for (const FCloudFileHeader& File : Files)
{
UE_LOG(LogTemp, Log, TEXT("%s (%d bytes)"),
*File.FileName, File.FileSize);
}
}
}));
}Delete File
Remove a file from cloud storage.

- Filename: Name of the file to delete
void UMyClass::DeleteCloudFile(const FString& Filename)
{
TSharedPtr<IEIKUserFile> UserFile = GetUserFileInterface();
FEIKDeleteUserFileParams Params;
Params.Filename = Filename;
UserFile->DeleteFile(0, Params,
FEIKAsyncCallback<void>::CreateLambda(
[Filename](const TEIKAsyncResult<void>& Result)
{
if (Result.IsSuccessful())
{
UE_LOG(LogTemp, Log, TEXT("Deleted: %s"), *Filename);
}
}));
}void UMyClass::DeleteCloudFile(const FString& Filename)
{
IUserFilePtr UserFile = GetUserFileInterface();
FUserFileDeleteFile::Params Params;
Params.LocalAccountId = GetLocalAccountId();
Params.Filename = Filename;
UserFile->DeleteFile(MoveTemp(Params))
.OnComplete([](const TOnlineResult<FUserFileDeleteFile>& Result) {});
}void UMyClass::DeleteCloudFile(const FString& Filename)
{
IOnlineSubsystem* OnlineSub = IOnlineSubsystem::Get(FName(TEXT("EIK")));
IOnlineUserCloudPtr UserCloud = OnlineSub->GetUserCloudInterface();
FUniqueNetIdPtr UserId = OnlineSub->GetIdentityInterface()->GetUniquePlayerId(0);
UserCloud->DeleteUserFile(*UserId, Filename, true, true);
}Helper Functions
The EIK User File Library provides utilities for working with cloud files:
| Function | Description |
|---|---|
SaveGameToBytes | Convert USaveGame to byte array |
BytesToSaveGame | Convert byte array back to USaveGame |
StringToBytes | Convert FString to byte array |
BytesToString | Convert byte array to FString |
JsonStringToBytes | Convert JSON string to bytes |
BytesToJsonString | Convert bytes to JSON string |
IsValidCloudFilename | Check if filename is valid for cloud storage |
Working with SaveGame Objects

- Create your SaveGame object
- Use Save Game to Bytes to convert it
- Pass the bytes to Write User File
To load:
- Use Read User File to get bytes
- Use Bytes to Save Game to convert back
- Cast to your SaveGame class
// Save
USaveGame* SaveGame = UGameplayStatics::CreateSaveGameObject(UMySaveGame::StaticClass());
TArray<uint8> Bytes;
UGameplayStatics::SaveGameToMemory(SaveGame, Bytes);
// Write Bytes to cloud...
// Load
// Read Bytes from cloud...
USaveGame* LoadedGame = UGameplayStatics::LoadGameFromMemory(Bytes);
UMySaveGame* MySave = Cast<UMySaveGame>(LoadedGame);