Unity SDK sample

This sample shows how to use the Cloud Save SDK 3.1 to save and load data.

You can find the sample in Unity by selecting Package ManagerCloud SaveSamples.

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using Unity.Services.Authentication;
using Unity.Services.CloudSave;
using Unity.Services.CloudSave.Models;
using Unity.Services.Core;
using UnityEngine;

namespace CloudSaveSample
{
    [Serializable]
    public class SampleObject
    {
        public string SophisticatedString;
        public int SparklingInt;
        public float AmazingFloat;
    }

    public class CloudSaveSample : MonoBehaviour
    {
        private async void Awake()
        {
            // Cloud Save needs to be initialized along with the other Unity Services that
            // it depends on (namely, Authentication), and then the user must sign in.
            await UnityServices.InitializeAsync();
            await AuthenticationService.Instance.SignInAnonymouslyAsync();

            // Player Data
            // first set of data saved without a write lock
            await ForceSaveSingleData("primitive_key", "value!");

            SampleObject firstSample = new SampleObject
            {
                AmazingFloat = 13.37f,
                SparklingInt = 1337,
                SophisticatedString = "hi there!"
            };
            string objectKey = "object_key";
            string writeLock = await ForceSaveObjectData(objectKey, firstSample);
            SampleObject incomingSample = await RetrieveSpecificData<SampleObject>(objectKey);
            Debug.Log(
                $"Loaded sample object: {incomingSample.AmazingFloat}, {incomingSample.SparklingInt}, {incomingSample.SophisticatedString}, write lock {writeLock}"
            );

            // second set of data saved with a write lock
            SampleObject secondSample = new SampleObject
            {
                AmazingFloat = 42.26f,
                SparklingInt = 4226,
                SophisticatedString = "hi there... again!"
            };
            string updatedWriteLock = await SaveObjectData(objectKey, secondSample, writeLock);
            SampleObject updatedSample = await RetrieveSpecificData<SampleObject>(objectKey);
            Debug.Log(
                $"Loaded updated sample object: {updatedSample.AmazingFloat}, {updatedSample.SparklingInt}, {updatedSample.SophisticatedString}, write lock {updatedWriteLock}"
            );

            // deletion with wrong write lock, this will fail with a validation error
            await DeleteSpecificData(objectKey, "incorrect-write-lock");

            // force delete without write lock
            await ForceDeleteSpecificData(objectKey);
            await ListAllKeys();
            await RetrieveEverything();

            // Custom Data, read-only
            string customTestId = "custom-test-id";
            await ListAllCustomKeys(customTestId);
            await RetrieveAllCustomData(customTestId);

            // Files
            var inputBytes = Encoding.UTF8.GetBytes("test content for file bytes");
            var inputStream = new MemoryStream(
                Encoding.UTF8.GetBytes("test content for file stream")
            );

            string bytesFileKey = "bytes-test-file";
            string streamFileKey = "stream-test-file";
            await SaveFileBytes(bytesFileKey, inputBytes);
            await SaveFileStream(streamFileKey, inputStream);

            await ListAllFiles();
            await GetFileMetadata(bytesFileKey);
            await GetFileMetadata(streamFileKey);

            var fileBytes = await LoadFileBytes(bytesFileKey);
            Debug.Log(
                $"Loaded sample file containing content: {Encoding.UTF8.GetString(fileBytes)}"
            );

            using var fileStream = await LoadFileStream(streamFileKey);
            using var streamReader = new StreamReader(fileStream);
            Debug.Log(
                $"Loaded sample file containing content: {await streamReader.ReadToEndAsync()}"
            );

            await DeleteFile(bytesFileKey);
            await DeleteFile(streamFileKey);
        }

        private async Task ListAllKeys()
        {
            try
            {
                var keys = await CloudSaveService.Instance.Data.Player.ListAllKeysAsync();

                Debug.Log($"Keys count: {keys.Count}\n" + $"Keys: {String.Join(", ", keys)}");
            }
            catch (CloudSaveValidationException e)
            {
                Debug.LogError(e);
            }
            catch (CloudSaveRateLimitedException e)
            {
                Debug.LogError(e);
            }
            catch (CloudSaveException e)
            {
                Debug.LogError(e);
            }
        }

        private async Task ListAllCustomKeys(string customId)
        {
            try
            {
                var keys = await CloudSaveService.Instance.Data.Custom.ListAllKeysAsync(customId);

                Debug.Log(
                    $"Keys count for custom ID {customId}: {keys.Count}\n" + $"Keys: {String.Join(", ", keys)}"
                );
            }
            catch (CloudSaveValidationException e)
            {
                Debug.LogError(e);
            }
            catch (CloudSaveRateLimitedException e)
            {
                Debug.LogError(e);
            }
            catch (CloudSaveException e)
            {
                Debug.LogError(e);
            }
        }

        private async Task ForceSaveSingleData(string key, string value)
        {
            try
            {
                Dictionary<string, object> oneElement = new Dictionary<string, object>();

                // It's a text input field, but let's see if you actually entered a number.
                if (Int32.TryParse(value, out int wholeNumber))
                {
                    oneElement.Add(key, wholeNumber);
                }
                else if (Single.TryParse(value, out float fractionalNumber))
                {
                    oneElement.Add(key, fractionalNumber);
                }
                else
                {
                    oneElement.Add(key, value);
                }

                // Saving the data without write lock validation by passing the data as an object instead of a SaveItem
                Dictionary<string, string> result =
                    await CloudSaveService.Instance.Data.Player.SaveAsync(oneElement);

                Debug.Log(
                    $"Successfully saved {key}:{value} with updated write lock {result[key]}"
                );
            }
            catch (CloudSaveValidationException e)
            {
                Debug.LogError(e);
            }
            catch (CloudSaveRateLimitedException e)
            {
                Debug.LogError(e);
            }
            catch (CloudSaveException e)
            {
                Debug.LogError(e);
            }
        }

        private async Task<string> ForceSaveObjectData(string key, SampleObject value)
        {
            try
            {
                // Although we are only saving a single value here, you can save multiple keys
                // and values in a single batch.
                Dictionary<string, object> oneElement = new Dictionary<string, object>
                {
                    { key, value }
                };

                // Saving data without write lock validation by passing the data as an object instead of a SaveItem
                Dictionary<string, string> result =
                    await CloudSaveService.Instance.Data.Player.SaveAsync(oneElement);
                string writeLock = result[key];

                Debug.Log($"Successfully saved {key}:{value} with updated write lock {writeLock}");

                return writeLock;
            }
            catch (CloudSaveValidationException e)
            {
                Debug.LogError(e);
            }
            catch (CloudSaveRateLimitedException e)
            {
                Debug.LogError(e);
            }
            catch (CloudSaveException e)
            {
                Debug.LogError(e);
            }

            return null;
        }

        private async Task<string> SaveObjectData(string key, SampleObject value, string writeLock)
        {
            try
            {
                // Although we are only saving a single value here, you can save multiple keys
                // and values in a single batch.
                // Use SaveItem to specify a write lock. The request will fail if the provided write lock
                // does not match the one currently saved on the server.
                Dictionary<string, SaveItem> oneElement = new Dictionary<string, SaveItem>
                {
                    { key, new SaveItem(value, writeLock) }
                };

                // Saving data with write lock validation by using a SaveItem with the write lock specified
                Dictionary<string, string> result = await CloudSaveService.Instance.Data.Player.SaveAsync(oneElement);
                string newWriteLock = result[key];

                Debug.Log(
                    $"Successfully saved {key}:{value} with updated write lock {newWriteLock}"
                );

                return newWriteLock;
            }
            catch (CloudSaveValidationException e)
            {
                Debug.LogError(e);
            }
            catch (CloudSaveRateLimitedException e)
            {
                Debug.LogError(e);
            }
            catch (CloudSaveException e)
            {
                Debug.LogError(e);
            }

            return null;
        }

        private async Task<T> RetrieveSpecificData<T>(string key)
        {
            try
            {
                var results = await CloudSaveService.Instance.Data.Player.LoadAsync(
                    new HashSet<string> { key }
                );

                if (results.TryGetValue(key, out var item))
                {
                    return item.Value.GetAs<T>();
                }
                else
                {
                    Debug.Log($"There is no such key as {key}!");
                }
            }
            catch (CloudSaveValidationException e)
            {
                Debug.LogError(e);
            }
            catch (CloudSaveRateLimitedException e)
            {
                Debug.LogError(e);
            }
            catch (CloudSaveException e)
            {
                Debug.LogError(e);
            }

            return default;
        }

        private async Task RetrieveEverything()
        {
            try
            {
                // If you wish to load only a subset of keys rather than everything, you
                // can call a method LoadAsync and pass a HashSet of keys into it.
                var results = await CloudSaveService.Instance.Data.Player.LoadAllAsync();

                Debug.Log($"{results.Count} elements loaded!");

                foreach (var result in results)
                {
                    Debug.Log($"Key: {result.Key}, Value: {result.Value.Value}");
                }
            }
            catch (CloudSaveValidationException e)
            {
                Debug.LogError(e);
            }
            catch (CloudSaveRateLimitedException e)
            {
                Debug.LogError(e);
            }
            catch (CloudSaveException e)
            {
                Debug.LogError(e);
            }
        }

        private async Task RetrieveAllCustomData(string customId)
        {
            try
            {
                // If you wish to load only a subset of keys rather than everything, you
                // can call a method LoadAsync and pass a HashSet of keys into it.
                var results = await CloudSaveService.Instance.Data.Custom.LoadAllAsync(customId);

                Debug.Log($"{results.Count} elements loaded from custom Id {customId}!");

                foreach (var result in results)
                {
                    Debug.Log($"Key: {result.Key}, Value: {result.Value.Value}");
                }
            }
            catch (CloudSaveValidationException e)
            {
                Debug.LogError(e);
            }
            catch (CloudSaveRateLimitedException e)
            {
                Debug.LogError(e);
            }
            catch (CloudSaveException e)
            {
                Debug.LogError(e);
            }
        }

        private async Task ForceDeleteSpecificData(string key)
        {
            try
            {
                // Deletion of the key without write lock validation by omitting the DeleteOptions argument
                await CloudSaveService.Instance.Data.Player.DeleteAsync(key);

                Debug.Log($"Successfully deleted {key}");
            }
            catch (CloudSaveValidationException e)
            {
                Debug.LogError(e);
            }
            catch (CloudSaveRateLimitedException e)
            {
                Debug.LogError(e);
            }
            catch (CloudSaveException e)
            {
                Debug.LogError(e);
            }
        }

        private async Task DeleteSpecificData(string key, string writeLock)
        {
            try
            {
                // Deletion of the key with write lock validation
                await CloudSaveService.Instance.Data.Player.DeleteAsync(
                    key,
                    new DeleteOptions { WriteLock = writeLock }
                );

                Debug.Log($"Successfully deleted {key}");
            }
            catch (CloudSaveValidationException e)
            {
                Debug.LogError(e);
            }
            catch (CloudSaveRateLimitedException e)
            {
                Debug.LogError(e);
            }
            catch (CloudSaveException e)
            {
                Debug.LogError(e);
            }
        }

        private async Task ListAllFiles()
        {
            try
            {
                var results = await CloudSaveService.Instance.Files.Player.ListAllAsync();

                Debug.Log("Metadata loaded for all files!");

                foreach (var element in results)
                {
                    Debug.Log($"Key: {element.Key}, File Size: {element.Size}");
                }
            }
            catch (CloudSaveValidationException e)
            {
                Debug.LogError(e);
            }
            catch (CloudSaveRateLimitedException e)
            {
                Debug.LogError(e);
            }
            catch (CloudSaveException e)
            {
                Debug.LogError(e);
            }
        }

        private async Task GetFileMetadata(string key)
        {
            try
            {
                var results = await CloudSaveService.Instance.Files.Player.GetMetadataAsync(key);

                Debug.Log("File metadata loaded!");

                Debug.Log($"Key: {results.Key}, File Size: {results.Size}");
            }
            catch (CloudSaveValidationException e)
            {
                Debug.LogError(e);
            }
            catch (CloudSaveRateLimitedException e)
            {
                Debug.LogError(e);
            }
            catch (CloudSaveException e)
            {
                Debug.LogError(e);
            }
        }

        private async Task SaveFileBytes(string key, byte[] bytes)
        {
            try
            {
                await CloudSaveService.Instance.Files.Player.SaveAsync(key, bytes);

                Debug.Log("File saved!");
            }
            catch (CloudSaveValidationException e)
            {
                Debug.LogError(e);
            }
            catch (CloudSaveRateLimitedException e)
            {
                Debug.LogError(e);
            }
            catch (CloudSaveException e)
            {
                Debug.LogError(e);
            }
        }

        private async Task SaveFileStream(string key, Stream stream)
        {
            try
            {
                await CloudSaveService.Instance.Files.Player.SaveAsync(key, stream);

                Debug.Log("File saved!");
            }
            catch (CloudSaveValidationException e)
            {
                Debug.LogError(e);
            }
            catch (CloudSaveRateLimitedException e)
            {
                Debug.LogError(e);
            }
            catch (CloudSaveException e)
            {
                Debug.LogError(e);
            }
        }

        private async Task<byte[]> LoadFileBytes(string key)
        {
            try
            {
                var results = await CloudSaveService.Instance.Files.Player.LoadBytesAsync(key);

                Debug.Log("File loaded!");

                return results;
            }
            catch (CloudSaveValidationException e)
            {
                Debug.LogError(e);
            }
            catch (CloudSaveRateLimitedException e)
            {
                Debug.LogError(e);
            }
            catch (CloudSaveException e)
            {
                Debug.LogError(e);
            }

            return null;
        }

        private async Task<Stream> LoadFileStream(string key)
        {
            try
            {
                var results = await CloudSaveService.Instance.Files.Player.LoadStreamAsync(key);

                Debug.Log("File loaded!");

                return results;
            }
            catch (CloudSaveValidationException e)
            {
                Debug.LogError(e);
            }
            catch (CloudSaveRateLimitedException e)
            {
                Debug.LogError(e);
            }
            catch (CloudSaveException e)
            {
                Debug.LogError(e);
            }

            return null;
        }

        private async Task DeleteFile(string key)
        {
            try
            {
                await CloudSaveService.Instance.Files.Player.DeleteAsync(key);

                Debug.Log("File deleted!");
            }
            catch (CloudSaveValidationException e)
            {
                Debug.LogError(e);
            }
            catch (CloudSaveRateLimitedException e)
            {
                Debug.LogError(e);
            }
            catch (CloudSaveException e)
            {
                Debug.LogError(e);
            }
        }
    }
}