# Stateful Cloud Code

> Build event-driven games using Stateful Cloud Code workflows with scopes.

> **Important:**
>
> This page describes an experimental feature that might change significantly before release. It's not recommended to use or rely on experimental features in production environments due to potential instability.

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.

> **Note:**
>
> Stateful Cloud Code requires you to reference the following NuGet package versions in your modules:
>
> * `com.unity.services.cloudcode.apis` v0.0.26 or later
> * `com.unity.services.cloudcode.core` v0.0.4 or later

## 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:

* `Player`: States are only accessible to a specific player and persist for the lifetime of that player. The default time to live for `Player` states is two years.
* `MultiplayerSession`: States are accessible to all players and persist for the lifetime of a game's session. The default time to live for `MultiplayerSession` states is seven days.

To configure scoped module functions, annotate your class with `StateScope` and a desired scope type, as demonstrated in the following code example:

```csharp title="Example of Player scope"
using Unity.Services.CloudCode.Core;

// 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;
    }
}
```

> **Important:**
>
> By default, the scope's state only serializes and persists the data for `public` members of your module. To allow serialization of `private` members, annotate them with `CloudCodeSerializeProperty` as shown in the previous example. Similarly, you can use `CloudCodeIgnoreProperty` to prevent serialization of `public` members.

Then, from your game, sign in to get a player ID, and reference that ID for future `Player`-scoped server module function calls:

```csharp title="Referencing player ID for future Player-scoped calls"
using UnityEngine;
using Unity.Services.Authentication;
using Unity.Services.Core;

// 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;
    }
}
```

Likewise, for `MultiplayerSession` scopes, [create a session](/mps-sdk/create-session.md) with `MultiplayerService`, and save a session ID for future scoped server module function calls:

```csharp title="Referencing session ID for future MultiplayerSession-scoped calls"
using UnityEngine;
using Unity.Services.Authentication;
using Unity.Services.Core;
using Unity.Services.Multiplayer;

// 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.

> **Note:**
>
> You need to re-generate your modules after adding `StateScope` annotations before you can implement them and call them from within your game.

The following code is an example of a Cloud Code function call incrementing a `Player`-scoped counter:

```csharp title="Incrementing player counter"
// Example of an in-game function call to the HelloPlayer module
public 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 using `CallModuleEndpointAsync()` with the Cloud Code Service instance. To make these calls, configure a `CloudCodeModuleScope` with the desired scope type and unique string ID targeting the player or group within the specified scope type.

```csharp title="Implementing player counter from CloudCodeService"
public 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 specified `ScopeType` other than the one deployed with the module, you will receive a `ScopeType` mismatch error.

This error can occur when your game client makes the following type of Cloud Code module function call:

* 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.

The following example of a `ScopeType` mismatch deploys the module with a `Player` session scope, but then calls from the perspective of a `MultiplayerSession` scope in the game.

```csharp title="Example of ScopeType mismatch"
using System.Threading.Tasks;
using Unity.Services.CloudCode.Core;

// Example setup for a HelloPlayer module with a Player scope
[StateScope(Scope.Player)]
public class HelloPlayer
{
    public int _counter;
    [CloudCodeFunction("IncrementPlayer")]
    public int IncrementPlayer()
    {
        return ++_counter;
    }
}

// In Game - Note the misconfigured MultiplayerSession Scope - ScopeType mismatch
public 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
}
```
