Stateful Cloud Code
Build event-driven games using Stateful Cloud Code workflows with scopes.
Read time 3 minutesLast updated 2 days ago
By default, Cloud Code is stateless, meaning that each time you run a script or module, Cloud Code doesn't remember the data from the previous run. However, for games that require persistent states, such as card, puzzle, and idle games, you can use scopes to preserve information during your scope's lifetime in Cloud Code.
Scopes can retain state for individual players, or a group of players in a multiplayer session.
Implementing Stateful Cloud Code ensures your game scales while operating at minimal cost.
Configuration
To implement state persistence, specify the scope type to determine how to retain your module's class variables within the lifecycle of the selected scope. There are two primary scope types available for configuration:- : States are only accessible to a specific player and persist for the lifetime of that player. The default time to live for
Playerstates is two years.Player - : States are accessible to all players and persist for the lifetime of a game's session. The default time to live for
MultiplayerSessionstates is seven days.MultiplayerSession
StateScope// Example module setup for a HelloPlayer module with a Player scope[StateScope(Scope.Player)]public class HelloPlayer{ [CloudCodeSerializeProperty] private int _counter; [CloudCodeFunction("IncrementPlayer")] public int IncrementPlayer() { return ++_counter; }}
Then, from your game, sign in to get a player ID, and reference that ID for future
PlayerLikewise, for// Example of logging into an anonymous account and referencing the// playerId to be invoked with for future Player-scoped calls.public class CallPlayerCloudCode : MonoBehaviour{ private string m_PlayerID; private async void Start() { await UnityServices.InitializeAsync(); await AuthenticationService.Instance.SignInAnonymouslyAsync(); // Access the currently logged-in player's ID m_PlayerID = AuthenticationService.Instance.PlayerId; }}
MultiplayerSessionMultiplayerService// Example of creating a client-hosted session and referencing the// sessionId to be invoked with for future MultiplayerSession-scoped calls.public class CallMultiplayerSessionCloudCode : MonoBehaviour{ private string m_SessionID; private async void Start() { await UnityServices.InitializeAsync(); await AuthenticationService.Instance.SignInAnonymouslyAsync(); var options = new SessionOptions { MaxPlayers = 2 }; var session = await MultiplayerService.Instance.CreateSessionAsync(options); Debug.Log($"Session {session.Id} created! Join code: {session.Code}"); // Access the session's ID m_SessionID = session.Id; }}
Scoped calls using generated modules
To target scoped module functions from your game client, initialize the module with the unique identifier corresponding to the desired player or multiplayer session. This initialization associates the module instance with that ID, ensuring that all subsequent function calls interact with the correct persistent state.
The following code is an example of a Cloud Code function call incrementing a
Player// Example of an in-game function call to the HelloPlayer modulepublic async Task OnButtonClickedHelloPlayer(){ try { var module = new HelloPlayerBindings(m_PlayerID, CloudCodeService.Instance); var result = await module.IncrementPlayer(); Debug.Log("Player " + "[" + m_PlayerID + $"] Counter: {result}"); } catch (CloudCodeException exception) { Debug.LogException(exception); }}
Scoped calls directly from CloudCodeService
You can also make calls directly to server endpoints without bindings usingCallModuleEndpointAsync()CloudCodeModuleScopepublic async Task OnButtonClickedHelloPlayerDirect(){ try { var scope = new CloudCodeModuleScope(ScopeType.Player, m_PlayerID); var result = await CloudCodeService.Instance.CallModuleEndpointAsync<int>( "HelloPlayer", "IncrementPlayer", scope: scope); Debug.Log("Player " + "[" + m_PlayerID + $"] Counter: {result}"); } catch (CloudCodeException exception) { Debug.LogException(exception); }}
ScopeType mismatch
If you try to make client calls with a specifiedScopeTypeScopeType- With a scope, but the deployed module has no scope attribute defined.
- Without a scope, but the deployed module has a defined scope attribute.
- With scope, but the deployed module's scope attribute doesn't match.
ScopeTypePlayerMultiplayerSession// Example setup for a HelloPlayer module with a Player scope[StateScope(Scope.Player)]public class HelloPlayer{ public int _counter; [CloudCodeFunction("IncrementPlayer")] public int IncrementPlayer() { // ... }}// In Game - Note the misconfigured MultiplayerSession Scope - ScopeType mismatchpublic async Task OnButtonClickedHelloPlayerDirect(){ var scope = new CloudCodeModuleScope(ScopeType.MultiplayerSession, m_SessionID); var result = await CloudCodeService.Instance.CallModuleEndpointAsync<int>( "HelloPlayer", "IncrementPlayer", scope: scope); // <--- Will have Error}