Overview
S3Vault connects your game to Amazon S3 without a server of your own. Players sign in to (or get a guest identity from) an Amazon Cognito identity pool, receive temporary AWS credentials, and read and write objects in your bucket — limited by an IAM policy to their own folder.
- Save Game To Cloud / Load Game From Cloud for any
Save Gameobject. - Files are streamed from and to disk, never loaded whole into memory; large files are uploaded in parallel parts.
- Downloads go to
<file>.partand are renamed when complete, so a failed or cancelled download never leaves a broken file. - Folder downloads for content packs, with Skip Existing to fetch only what changed in size.
- List, inspect, copy and delete objects; presigned GET and PUT links.
- Every node is latent and cancellable; requests run on worker threads and results arrive on the game thread.
S3Vault has 28 Blueprint nodes: 17 latent nodes, 9 configuration and identity functions and the Cancel / Is Finished functions of every latent node.
Requirements
| Requirement | Details |
|---|---|
| Unreal Engine | 4.27 and 5.8 (one source, verified clean on both) |
| Modules | S3Vault (Runtime), S3VaultSdk (External — the prebuilt AWS SDK for C++ 1.11.916) |
| Dependencies | None (engine modules only; no other plugins are enabled) |
| Platforms | Win64, Linux, Android. On Linux and Android the SDK uses the engine's libcurl, OpenSSL and certificate store. |
| AWS | An AWS account with an S3 bucket and (for players) a Cognito identity pool. S3 storage, requests, data transfer and Cognito usage are billed by AWS. |
Installation
- Install S3Vault from Fab into your engine, or copy the
S3Vaultfolder into your project'sPluginsfolder. - Open the project, enable S3Vault in Edit > Plugins and restart the editor.
- Open Project Settings > Plugins > S3Vault and set Region, Identity Pool Id, Default Bucket and Key Prefix.
- C++ only: add
PublicDependencyModuleNames.Add("S3Vault");to yourBuild.cs.
Setting up AWS
- Bucket. In the S3 console create a bucket (for example
my-game-saves) in the region you set in the project settings. Keep Block all public access enabled — players reach the bucket with their own credentials, not publicly. - Identity pool. In Amazon Cognito create an identity pool. Enable guest (unauthenticated) access if players should save before signing in, and add the identity providers you use (a Cognito user pool, Google, Apple, Facebook or OpenID). Copy the pool id (
us-east-1:0123...) to Identity Pool Id. - Role policy. Attach the policy below to the pool's authenticated role (and to the guest role if you allow guests). Replace
my-game-saves, and use the same folder as your Key Prefix (players/{IdentityId}). - Optional: enable Transfer Acceleration on the bucket and tick Use Transfer Acceleration for faster uploads from far-away players.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ListOwnFolderAndContent",
"Effect": "Allow",
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::my-game-saves",
"Condition": {
"StringLike": {
"s3:prefix": [
"players/${cognito-identity.amazonaws.com:sub}/*",
"dlc/*"
]
}
}
},
{
"Sid": "ReadWriteOwnFiles",
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:DeleteObject",
"s3:AbortMultipartUpload",
"s3:ListMultipartUploadParts"
],
"Resource": "arn:aws:s3:::my-game-saves/players/${cognito-identity.amazonaws.com:sub}/*"
},
{
"Sid": "ReadSharedContent",
"Effect": "Allow",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::my-game-saves/dlc/*"
}
]
}
${cognito-identity.amazonaws.com:sub} is the player's Cognito identity id — exactly what S3Vault puts in place of {IdentityId}, so every player can only list, read, write and delete below their own players/<identity id>/ folder. The dlc/ statements give every player read access to shared content (download it with an absolute key such as /dlc/pack1/); drop them if you do not ship content packs.
DefaultGame.ini and in every packaged build. They are refused in Shipping builds unless Allow Static Keys In Shipping is set — use that only for dedicated servers.Keys & prefixes
Every node takes a Bucket (empty = Default Bucket) and a Key. Keys are relative to the Key Prefix unless they start with /:
| Key Prefix | Key | S3 key |
|---|---|---|
players/{IdentityId} | saves/slot1.sav | players/us-east-1:1a2b.../saves/slot1.sav |
players/{IdentityId} | /dlc/pack1/Pack1.pak | dlc/pack1/Pack1.pak |
{Region}/shared | motd.json | us-east-1/shared/motd.json |
| (empty) | saves/slot1.sav | saves/slot1.sav |
Tokens: {IdentityId} = the player's Cognito identity id (resolved automatically before every request in Cognito Identity Pool mode; in the other modes a key that uses it fails with IdentityUnavailable), {Region} = the configured region. Resolve Key shows the final key. Keys returned by the nodes (Info > Key, list results, failed keys) use the same relative form, so you can pass them straight back; Info > Full Key holds the complete S3 key.
Prefixes work like folders: saves/ matches everything in the saves folder, saves also matches saves-old/.... The folder nodes always treat their prefix as a folder.
Local paths that are relative are relative to the project's Saved folder; absolute paths are used as given (for content packs, Project Persistent Download Dir is a good choice on every platform).
Quick start
Blueprint
- Create your
Save Gameobject as usual (Create Save Game Object) and fill it. - Add Save Game To Cloud, connect the object and set Key to
saves/slot1.sav. - To load, add Load Game From Cloud with the same Key and cast Save Game on On Success to your class.
- On On Failure, check Error > Code:
NoSuchKeymeans there is no cloud save yet. - For big files use Upload File / Download File and bind On Progress to a progress bar (Bytes Done / Total Bytes).
C++
// Build.cs: PublicDependencyModuleNames.Add("S3Vault");
#include "S3VaultAsyncActions.h"
void AMyGameMode::DownloadPack()
{
US3VaultDownloadFolderAction* Action = US3VaultDownloadFolderAction::DownloadFolder(this,
TEXT("/dlc/pack1/"), FPaths::ProjectPersistentDownloadDir() / TEXT("Pack1"), FString(), /*bSkipExisting*/ true);
Action->OnProgress.AddDynamic(this, &AMyGameMode::OnPackProgress); // (int32 FilesDone, int32 FilesTotal)
Action->OnSuccess.AddDynamic(this, &AMyGameMode::OnPackDone); // (int32 FileCount, const TArray<FString>& FailedFiles, const FS3VaultError& Error)
Action->OnFailure.AddDynamic(this, &AMyGameMode::OnPackDone);
Action->Activate();
}
Every latent node is a UBlueprintAsyncActionBase: call its static factory, bind the delegates (they are dynamic, so the handlers must be UFUNCTIONs), then call Activate(). Keep the returned pointer if you want to call Cancel().
Uploads & downloads
| Node | Type | Description |
|---|---|---|
| Upload File | Latent | Pins: Local Path, Key, advanced Content Type (empty = from the extension), Metadata (user metadata, sent as x-amz-meta-*), Bucket. Streams the file; above Multipart Threshold MB it is uploaded in parallel 8 MB parts. On Progress (Bytes Done, Total Bytes), On Success / On Failure (Info, Error). |
| Upload Bytes | Latent | Pins: Data (byte array), Key, Content Type (empty = application/octet-stream), Metadata, Bucket. |
| Upload String | Latent | Pins: Text (sent as UTF-8), Key, Metadata, Bucket, Content Type (default text/plain; charset=utf-8; use application/json for JSON). |
| Download File | Latent | Pins: Key, Local Path, Bucket. Writes to <Local Path>.part, renames it when complete and creates missing folders. On Progress (Bytes Done, Total Bytes), On Success / On Failure (Info, Error). |
| Download Bytes | Latent | Pins: Key, Bucket. Returns Data, Info, Error. For objects that fit in memory. |
| Download String | Latent | Pins: Key, Bucket. Returns Text (UTF-8 decoded, byte order mark removed), Info, Error. |
Progress is reported at most about 10 times per second, always on the game thread. Cancel stops the request immediately; a cancelled multipart upload is aborted on S3 so no orphaned parts are billed.
Save games
| Node | Type | Description |
|---|---|---|
| Save Game To Cloud | Latent | Pins: Save Game, Key, Bucket. Serializes the object with the engine's Save Game To Memory (the same format as Save Game To Slot) when the node is called, then uploads it. |
| Load Game From Cloud | Latent | Pins: Key, Bucket. Downloads and recreates the object with Load Game From Memory. On Success (Save Game, Info, Error), On Failure — InvalidSaveGame when the data is not a save game or its class no longer exists. |
saves/slot1.bak) before you overwrite a save.Folders & content packs
| Node | Type | Description |
|---|---|---|
| Download Folder | Latent | Pins: Prefix, Local Directory, Bucket, advanced Skip Existing. Lists every object below the prefix and downloads them one by one, keeping sub folders. Skip Existing keeps local files whose size matches. Keys that are not safe relative paths (.., drive letters) are skipped and reported. On Progress (Files Done, Files Total), On Success / On Failure (File Count, Failed Files, Error). |
| Upload Folder | Latent | Pins: Local Directory, Prefix, Bucket. Uploads every file (recursively, .part files excluded) with content types from the extensions; large files use multipart uploads. Same output pins as Download Folder. |
A failed file does not stop the others: the node finishes with On Failure, the number of files that succeeded and the list of failed files, and Error describes the first failure.
Object management
| Node | Type | Description |
|---|---|---|
| List Objects | Latent | Pins: Prefix, Continuation Token, Bucket, advanced Delimiter (default /) and Max Keys (1–1000). Result holds Objects, Common Prefixes (sub folders when a delimiter is set), Next Token and Truncated. Pass Next Token back as Continuation Token for the next page. |
| Get Object Info | Latent | Pins: Key, Bucket. Size, last modified, ETag, storage class, content type and metadata without downloading. On Not Found fires when the key does not exist. With the per-player policy S3 answers 403 instead of 404 for missing keys; S3Vault then checks with a one-key list request and still reports Not Found (downloads report NoSuchKey the same way), so keep the s3:ListBucket statement in the policy. |
| Copy Object | Latent | Pins: Source Key, Dest Key, advanced Source Bucket (empty = the destination bucket) and Bucket. Server-side copy up to 5 GB; content type and metadata are kept. |
| Delete Object | Latent | Pins: Key, Bucket. Deleting a key that does not exist also succeeds. |
| Delete Objects | Latent | Pins: Keys, Prefix, Bucket. Deletes every key in Keys, or — when Keys is empty — every object below Prefix, in requests of 1000 keys. Returns Deleted Count and Failed Keys. With no Key Prefix and an empty Prefix the node refuses to run; pass Prefix / to empty the whole bucket on purpose. |
| Get Presigned URL | Latent | Pins: Key, Bucket, Verb (GET or PUT), Expires Seconds (default 3600, at most 7 days). Returns a URL anyone can use without credentials until it expires. A PUT URL uploads the request body as the object. With Cognito credentials the URL also stops working when the temporary credentials expire (at most one hour). |
Identity & configuration
| Node | Type | Description |
|---|---|---|
| Resolve Identity | Latent | Pin: Force Refresh. Fetches the Cognito identity and temporary credentials now (every request also does this on demand) and returns the Identity Id. |
| Set Login Token | Callable | Signs the player in to the identity pool: Provider is the pool's login key (for example accounts.google.com, appleid.apple.com, or see Make User Pool Provider Name), Token the provider's ID token. The cached identity is replaced. |
| Clear Login Tokens | Callable | Forgets all tokens and the identity; the player is a guest again. |
| Set Static Credentials | Callable | Uses the given keys (and optional session token) from now on and switches to Static Keys mode — for example temporary keys handed out by your own backend. |
| Set Region | Callable | Overrides the region for every following request. |
| Set Credential Mode | Callable | Overrides the credential mode at runtime. |
| Get Identity Id | Pure | The player's Cognito identity id; empty until credentials were resolved once. |
| Make User Pool Provider Name | Pure | Builds cognito-idp.<region>.amazonaws.com/<user pool id> (the region is taken from the pool id when empty). |
| Is Configured | Pure | False with a Problem text when the settings are incomplete. |
| Resolve Key | Pure | The S3 key a node would use for a Key (prefix and tokens applied). {IdentityId} stays as written until the identity was resolved. |
| Cancel | Callable | On any latent node: stops the request or transfer; none of its pins fire afterwards. |
| Is Finished | Pure | On any latent node: true after it completed or was cancelled. |
Types
| Type | Kind | Description |
|---|---|---|
| FS3VaultObjectInfo | Struct | Key (relative form), Full Key, Size (int64 bytes), Last Modified (UTC), ETag, Storage Class, Content Type and Metadata (filled by uploads, downloads and Get Object Info). |
| FS3VaultListResult | Struct | Objects, Common Prefixes, Next Token, Truncated. |
| FS3VaultError | Struct | Code, Message, Http Status (0 when AWS was not reached), Retryable. |
| ES3VaultCredentialMode | Enum | Cognito Identity Pool, Static Keys, Default Credential Chain. |
| ES3VaultUrlVerb | Enum | GET, PUT. |
Settings
Project Settings > Plugins > S3Vault (saved to DefaultGame.ini). Changes apply to the next request.
| Setting | Default | Description |
|---|---|---|
| Region | us-east-1 | Region of the bucket and the identity pool. The bucket must be in this region. |
| Credential Mode | Cognito Identity Pool | Where request credentials come from. |
| Identity Pool Id | — | The Cognito identity pool. |
| Allow Guest Access | true | Use guest identities before Set Login Token (the pool must allow them too). |
| Access Key Id / Secret Access Key | — | Static Keys mode (development and servers). |
| Allow Static Keys In Shipping | false | Advanced. Static keys from the settings are refused in Shipping builds unless enabled. |
| Default Bucket | — | Bucket used when a node leaves Bucket empty. |
| Key Prefix | — | Folder for relative keys, with the {IdentityId} and {Region} tokens, for example players/{IdentityId}. |
| Multipart Threshold MB | 16 | Files larger than this are uploaded in parallel 8 MB parts (5–4096). The transfer buffers (32 MB) are allocated on the first multipart upload. |
| Use Transfer Acceleration | false | Use S3 Transfer Acceleration endpoints. Must be enabled on the bucket; ignored with an Endpoint Override. |
| Endpoint Override | — | Advanced. A local S3-compatible emulator such as http://localhost:4566 (path-style addressing). |
| Request Timeout Ms | 30000 | Longest time a request may go without receiving data. |
| Connect Timeout Ms | 5000 | Time allowed to open a connection. |
| Max Retries | 3 | Automatic retries with back-off for throttling and transient errors. |
| Worker Threads | 4 | Advanced. Requests that run at the same time. |
| Verify SSL | true | Advanced. Only disable for a local emulator. |
| Log Sdk Warnings | true | Advanced. SDK warnings and errors go to the Output Log (LogS3Vault). |
Error codes
| Code | Meaning |
|---|---|
NoSuchKey | The object does not exist (downloads). |
NotFound | Get Object Info: the object does not exist (On Not Found). |
AccessDenied | The IAM policy does not allow this key or action — check the Key Prefix against the policy. |
NoSuchBucket / PermanentRedirect | Wrong bucket name, or the bucket is in another region than the settings. |
NotConfigured / NotSignedIn | Settings incomplete, or guest access disabled before Set Login Token. |
IdentityUnavailable | The key uses {IdentityId} but there is no Cognito identity (Static Keys or Default Chain mode). |
InvalidKey / InvalidPrefix / InvalidPath | Empty key, key ending in /, unsafe bulk delete, or no local path. |
FileNotFound / FileOpenFailed / WriteFailed / DirectoryNotFound | Local file system problems. |
InvalidSaveGame / TooLarge | Save game could not be (de)serialized; object too large for Download Bytes. |
Cancelled / Unavailable / ShuttingDown / NetworkError | Cancelled by you, SDK library missing or module shutting down, or no connection (Retryable = true). |
Troubleshooting
| Symptom | Cause and fix |
|---|---|
AccessDenied for every key | The Key Prefix does not match the policy. Use players/{IdentityId} with the policy above, and attach the policy to the role the player actually gets (guest or authenticated). |
| Saves "disappear" after sign-in | The signed-in player has a new identity id and therefore a new folder. See Signed-in players get a different identity above. |
PermanentRedirect | The bucket is in another region. Set Region to the bucket's region (or call Set Region). |
Uploads larger than the threshold fail with AccessDenied | Multipart uploads need s3:PutObject and, for clean-up, s3:AbortMultipartUpload on the same keys. |
| A presigned URL returns 403 after a while | It was signed with temporary Cognito credentials, which expire within an hour regardless of Expires Seconds. |
A .part file is left next to a download | The game was closed during the download. It is overwritten by the next download and never uploaded by Upload Folder. |
Requests fail with Unavailable on Windows | S3VaultSdk.dll could not be loaded — the Output Log has the path. Reinstall the plugin so Source/ThirdParty/S3VaultSdk/Win64 is complete. |
Credits
© 2026 Alpha XP — alphaxp.net · Support: support@alphaxp.net. Built and verified for Unreal Engine 4.27 and 5.8. Includes the AWS SDK for C++ 1.11.916 (Apache-2.0).