Remote saving is asynchronous

A remote load can't run synchronously, so a remote manager is not ready the moment its asset comes into scope, and it keeps retrying until that load lands. For more information, refer to When the data is ready.

Server (HTTP) and Cloud Save (UGS) can also persist through a local cache, which you pick with Cache Mode. A save then lands on the device first, which means it succeeds offline, and a background loop pushes it to the server.

YOUR GAME save ON DEVICE Local cache REMOTE Server write instant, durable push (background) retries until confirmed

With nothing cached and the server unreachable, Offline Cold Start decides what that first load does:

  • Wait For Server keeps the load retrying until the server answers, so a reinstalled device restores its backup rather than starting over it. A brand-new player can't start offline.
  • Start Fresh starts the player on the authored values, so a new player can play offline straight away. The first load that does reach a server already holding a save settles it as an ordinary conflict: IMergeable folds the offline progress into what the server holds, and without it the most recent save wins.

While the game runs, Status says whether everything has reached the server, whether changes are waiting, or whether one is held. OnStatusChanged fires on every change, and PushPendingChangesAsync() flushes what is waiting.

Resolve a conflict between two devices

The package never pushes a pending change over a save it hasn't seen, so a second device that played offline can't overwrite the first. It holds the change until the next load settles it.

Three things settle a conflict, and the first that applies wins:

  1. IMergeable folds both saves together.
  2. ConflictPolicy.Ask asks the player.
  3. ConflictPolicy.PreferNewest, the default, takes the most recent save, which relies on the two devices' clocks.

Set Conflict Policy to Ask on the manager, then answer Conflicts.OnAny with Conflicts.Resolve(manager, KeepLocal | KeepRemote). Both saves arrive as SaveNodes with their save times, and the load stays undecided until you answer.

GameObject > Persistent Asset > Conflict Prompt (IMGUI) and Conflict Prompt (uGUI) are ready-made panels, which tell the two saves apart by the values you list in Preview Fields. For how to pick one, refer to Build a save menu.

  • The IMGUI one doesn't stop the game around it, so hold your own UI off while IsAsking is true.
  • The uGUI one blocks the UI behind it, and is driven by a PersistenceConflictListener.
Note: Set Ask only with one of those panels in the scene or something else listening, or the load settles as Prefer Newest anyway.

Merge two saves

IMergeable folds the other device's save into yours instead of discarding one of them. Merge reads the other side as a SaveNode, applies what it wants from it, and returns whether it changed anything.

Warning: Merge must be commutative and idempotent. The result can't depend on which device folds first, and folding the same save twice must change nothing. A maximum or a set union is safe, while coins += otherCoins pays the player twice.
Warning: Return true only when you changed something, or two devices save back and forth forever.

Server (HTTP)

Server (HTTP) saves to your own server, at url/slot/id. That address needs three routes:

  • GET returns the stored body, and 404 when there is none.
  • PUT stores the request body verbatim.
  • DELETE removes it.
Server (HTTP) manager inspector

Sign-in is your game's job. Set the credentials as soon as it returns:

HttpPersistenceManager.DefaultAuthorization = "Bearer " + token;    // every HTTP manager
leaderboardManager.Authorization = "Bearer " + otherToken;          // or just this one

A backend service wraps your data in a JSON envelope of its own, which the manager's Backend Service fields describe. For copy-ready values per service, refer to Connect to a backend service.

Warning: The body must come back byte for byte. A server that trims whitespace or re-encodes the body breaks loading.
Note: Use DefaultConfigureRequest when a request needs other headers.

Steam Cloud

Steam offers two ways to store a save, and the package supports both:

  • Auto-Cloud needs no code and no plugin. Steam uploads the folders you name when the game exits and restores them on the next machine, so the game keeps its Local File manager.
  • The Steam Cloud manager writes each save through Steam Remote Storage. It needs Steamworks.NET and SteamAPI.Init, and it can report the quota and list what the cloud holds.

To set up Auto-Cloud, point its rule in Steamworks at the folder a Local File manager writes to, and add a path override per platform, taking each value from a real build's Application.persistentDataPath:

Steamworks fieldValue
Root PathWinAppDataLocalLow
Subdirectory{CompanyName}/{ProductName}, from your Player Settings
Pattern*.save, or the manager's File Extension
RecursiveOn. The manager writes into a Save sub-folder, so with this off the rule matches nothing

Steam syncs in the background and settles conflicts between machines itself, so the Steam Cloud manager needs no offline cache. With Steam Cloud switched off for the account or the application, saves stay on the machine. Outside Steam every operation fails, so route a DRM-free build to a Local File manager.

Warning: Auto-Cloud syncs on exit, so a crash or a kill from the Steam overlay can lose that session.
Warning: The Auto-Cloud quota is per application and small by default: raise it in Steamworks and turn compression on, because a save that doesn't fit is dropped silently.

Cloud Save (UGS)

Cloud Save stores one key per save in the signed-in player's data, so Unity Services must be initialized and a player signed in before any persistent asset comes into scope.

Make the player id the slot, and set it while the old player is still signed in. The cache is keyed by manager and slot, which also stops one player's cached save reaching another on a shared device.

Note: An anonymous player id lives on the device, and uninstalling deletes it, so link the account to a platform identity for a save that survives a reinstall.
Note: A player who is not signed in yet is a retry rather than a failure: the load recovers once sign-in lands.
Warning: A save past Cloud Save's size limit fails quietly and keeps retrying, so turn Compression on.