# Save and load data

> Call a Cloud Code module from a Unity client to save and load Player Data and Game Data in Cloud Save.

You can save and load data with Cloud Code by calling a Cloud Code module from a Unity client. The module reads and writes data in [Cloud Save](/cloud-save.md), and the client loads that data back. For background on the difference between Player Data and Game Data, and which token each API uses, refer to [Integrate with other Unity services](../how-to-guides/unity-services-integration).

To save and load data with Cloud Code, perform the following tasks:

1. [Create the Cloud Code module](#create-the-cloud-code-module)
2. [Test the Cloud Code module](#test-the-cloud-code-module)
3. [Verify the Cloud Save data](#verify-the-cloud-save-data)

## Prerequisites

* Follow the [get started](../getting-started) guide to generate a Cloud Code module. Name the module reference file `SaveLoadExample`.
* Install the [Com.Unity.Services.CloudCode.Apis](https://www.nuget.org/packages/Com.Unity.Services.CloudCode.Apis) NuGet package in the module project. This package provides the `IGameApiClient` interface used in this example.

## Create the Cloud Code module

Create a Cloud Code module that saves and loads data in Cloud Save through the `GameApiClient` class. The module exposes the following functions:

* `SavePlayerData` and `GetPlayerData`: save and read Player Data, tied to the calling player and authenticated with `context.AccessToken`.
* `SaveGameData` and `GetGameData`: save and read a shared Game Data record, authenticated with `context.ServiceToken`.

> **Note:**
>
> `SaveGameData` and `GetGameData` write to and read from a single, fixed Game Data record (`global`/`seasonalEvent`) instead of an identifier supplied by the caller. This avoids exposing an open `ServiceToken`-authenticated endpoint that could target arbitrary records. If your game needs to expose more than one Game Data record to a client, add [Access Control](../how-to-guides/access-control) rules to restrict which records and keys a player can target.

```csharp
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Unity.Services.CloudCode.Apis;
using Unity.Services.CloudCode.Apis.Extensions;
using Unity.Services.CloudCode.Core;
using Unity.Services.CloudCode.Shared;
using Unity.Services.CloudSave.Model;

namespace SaveLoadExample;

public class SaveLoadModule
{
    private readonly ILogger<SaveLoadModule> _logger;

    public SaveLoadModule(ILogger<SaveLoadModule> logger)
    {
        _logger = logger;
    }

    [CloudCodeFunction("SavePlayerData")]
    public async Task SavePlayerData(IExecutionContext context, IGameApiClient gameApiClient, string key, string value)
    {
        try
        {
            await gameApiClient.CloudSaveData.SetItemAsync(
                context,
                context.AccessToken,
                context.ProjectId,
                context.PlayerId!,
                new SetItemBody(key, value));

            _logger.LogInformation("Successfully saved player data for key: {Key}", key);
        }
        catch (ApiException ex)
        {
            _logger.LogError("Failed to save player data for key {Key}. Error: {Error}", key, ex.Message);
            throw new Exception($"Unable to save player data: {ex.Message}");
        }
    }

    [CloudCodeFunction("GetPlayerData")]
    public async Task<string> GetPlayerData(IExecutionContext context, IGameApiClient gameApiClient, string key)
    {
        try
        {
            var result = await gameApiClient.CloudSaveData.GetItemsAsync(
                context,
                context.AccessToken,
                context.ProjectId,
                context.PlayerId!,
                new List<string> { key });

            var data = result.Data.Results.FirstOrDefault()?.Value?.ToString() ?? string.Empty;
            _logger.LogInformation("Successfully retrieved player data for key: {Key}", key);
            return data;
        }
        catch (ApiException ex)
        {
            _logger.LogError("Failed to retrieve player data for key {Key}. Error: {Error}", key, ex.Message);
            throw new Exception($"Unable to retrieve player data: {ex.Message}");
        }
    }

    [CloudCodeFunction("SaveGameData")]
    public async Task SaveGameData(IExecutionContext context, IGameApiClient gameApiClient, string value)
    {
        try
        {
            await gameApiClient.CloudSaveData.SetCustomItemAsync(
                context,
                context.ServiceToken,
                context.ProjectId,
                "global",
                new SetItemBody("seasonalEvent", value));

            _logger.LogInformation("Successfully saved game data for the seasonalEvent key");
        }
        catch (ApiException ex)
        {
            _logger.LogError("Failed to save game data for the seasonalEvent key. Error: {Error}", ex.Message);
            throw new Exception($"Unable to save game data: {ex.Message}");
        }
    }

    [CloudCodeFunction("GetGameData")]
    public async Task<string> GetGameData(IExecutionContext context, IGameApiClient gameApiClient)
    {
        try
        {
            var result = await gameApiClient.CloudSaveData.GetCustomItemsAsync(
                context,
                context.ServiceToken,
                context.ProjectId,
                "global",
                new List<string> { "seasonalEvent" });

            var data = result.Data.Results.FirstOrDefault()?.Value?.ToString() ?? string.Empty;
            _logger.LogInformation("Successfully retrieved game data for the seasonalEvent key");
            return data;
        }
        catch (ApiException ex)
        {
            _logger.LogError("Failed to retrieve game data for the seasonalEvent key. Error: {Error}", ex.Message);
            throw new Exception($"Unable to retrieve game data: {ex.Message}");
        }
    }
}

public class ModuleConfig : ICloudCodeSetup
{
    public void Setup(ICloudCodeConfig config)
    {
        config.AddGameApiClient();
    }
}
```

## Test the Cloud Code module

Call the module from a Unity client to confirm that the save and load functions work as expected.

[Generate bindings](../getting-started#generate-bindings) and [deploy the module](../getting-started#deploy-the-module).

Next, define a `MonoBehaviour` script that calls the module to save and then load both Player Data and Game Data:

```cs
using Unity.Services.Authentication;
using Unity.Services.CloudCode;
using Unity.Services.CloudCode.GeneratedBindings;
using Unity.Services.Core;
using UnityEngine;

public class TestModule : MonoBehaviour
{
    private async void Start()
    {
        // Initialize the Unity Services Core SDK
        await UnityServices.InitializeAsync();

        // Authenticate by logging into an anonymous account
        await AuthenticationService.Instance.SignInAnonymouslyAsync();

        try
        {
            var module = new SaveLoadExampleBindings(CloudCodeService.Instance);

            // Save and load Player Data
            await module.SavePlayerData("playerScore", "100");
            var playerScore = await module.GetPlayerData("playerScore");
            Debug.Log($"Loaded player data: {playerScore}");

            // Save and load Game Data
            await module.SaveGameData("harvestFestival");
            var seasonalEvent = await module.GetGameData();
            Debug.Log($"Loaded game data: {seasonalEvent}");
        }
        catch (CloudCodeException exception)
        {
            Debug.LogException(exception);
        }
    }
}
```

Attach the script to a GameObject in your scene, then enter Play mode. The console logs the values loaded back from Cloud Save, confirming that the saved data loads back correctly.

## Verify the Cloud Save data

Confirm that the values the client saved actually reached Cloud Save.

To verify the saved data:

1. In the [Unity Dashboard](https://cloud.unity.com/cloud-save), go to **Development** > **Products** > **Cloud Save**.
2. Select **Player Data**, find the authenticated player's entry, and confirm the `playerScore` key has the value `100`.
3. Select **Game Data**, find the `global` entry, and confirm the `seasonalEvent` key has the value `harvestFestival`.
