# Cloud Storage

**Cloud Storage** is a general data storage service offered within the **coherence Cloud**.

## Storage Object Identifier

Objects stored in cloud storage are identified using a tuple called **storage object identifier** or `StorageObjectId`.

The storage object identifier consists of a **type** and an **id**. **Type** is intended to describe the general category that the object belongs to - for example, "Player" or "Settings" - and **id** is intended to uniquely distinguish the object from all other objects with the same **type**.

For example, to store player-specific data in cloud storage, you can set **id** to be the [identifier of a player account](https://unityapi.coherence.io/docs/v2.1.0/api/Coherence.Cloud.PlayerAccount.Id.html#Coherence_Cloud_PlayerAccount_Id):, as shown in the [Player Accounts](https://docs.coherence.io/hosting/authentication-service-player-accounts#player-account-id) section.

{% hint style="info" %}
`StorageObjectId` type is serializable — can be shown and modified through the Inspector.
{% endhint %}

## Usage

{% hint style="info" %}
You must be logged into the coherence Cloud to use this service. Read more about authentication on the [Player Accounts](https://docs.coherence.io/hosting/coherence-cloud/authentication-service-player-accounts) page.
{% endhint %}

[The CloudStorage API](https://unityapi.coherence.io/docs/v2.1.0/api/Coherence.Cloud.CloudStorage.html) provides three methods, allowing you to [save](https://unityapi.coherence.io/docs/v2.1.0/api/Coherence.Cloud.CloudStorage.SaveObjectAsync.html#Coherence_Cloud_CloudStorage_SaveObjectAsync_Coherence_Cloud_StorageObject_System_Threading_CancellationToken_), [load](https://unityapi.coherence.io/docs/v2.1.0/api/Coherence.Cloud.CloudStorage.LoadObjectAsync.html#Coherence_Cloud_CloudStorage_LoadObjectAsync_Coherence_Cloud_StorageObjectId_System_Threading_CancellationToken_) and [delete](https://unityapi.coherence.io/docs/v2.1.0/api/Coherence.Cloud.CloudStorage.DeleteObjectAsync.html#Coherence_Cloud_CloudStorage_DeleteObjectAsync_Coherence_Cloud_StorageObjectId_System_Threading_CancellationToken_) objects.

### **Saving Objects**

The [SaveObjectAsync](https://unityapi.coherence.io/docs/v2.1.0/api/Coherence.Cloud.CloudStorage.LoadObjectAsync.html#Coherence_Cloud_CloudStorage_LoadObjectAsync_Coherence_Cloud_StorageObjectId_System_Threading_CancellationToken_) method can be used to save an object to cloud storage.

```csharp
using Coherence.Toolkit;
using UnityEngine;

class CloudSaveExample : MonoBehaviour
{
    public StorageObjectId objectId = ("Greeter", 1);

    async void Start()
    {
        // Get main player account once it has logged in.
        PlayerAccount playerAccount = await PlayerAccount.GetMainAsync();
        
        // Save the string "Hello, World!" in cloud storage:
        var cloudStorage => playerAccount.Services.GameServices.CloudStorage;
        await cloudStorage.SaveObjectAsync(objectId, "Hello, World!");
    }
}
```

### **Loading Objects**

The [LoadObjectAsync](https://unityapi.coherence.io/docs/v2.1.0/api/Coherence.Cloud.CloudStorage.LoadObjectAsync.html#Coherence_Cloud_CloudStorage_LoadObjectAsync_Coherence_Cloud_StorageObjectId_System_Threading_CancellationToken_) method can be used to load an object from cloud storage.

```csharp
// Load a string from cloud storage:
string text = await cloudStorage.LoadObjectAsync(objectId);
Debug.Log(text);
```

### **Deleting Objects**

The [DeleteObjectAsync](https://unityapi.coherence.io/docs/v2.1.0/api/Coherence.Cloud.CloudStorage.DeleteObjectAsync.html#Coherence_Cloud_CloudStorage_DeleteObjectAsync_Coherence_Cloud_StorageObjectId_System_Threading_CancellationToken_) method can be used to delete an object from cloud storage.

```csharp
// Delete a string from cloud storage:
await cloudStorage.DeleteObjectAsync(objectId);
```

## Storage Operation

All of CloudStorage's methods return a [StorageOperation](https://unityapi.coherence.io/docs/v2.1.0/api/Coherence.Cloud.StorageOperation.html) or a [StorageOperation\<TResult>](https://unityapi.coherence.io/docs/v2.1.0/api/Coherence.Cloud.StorageOperation-1.html).

### **Async Methods**

These are [Task-like](https://learn.microsoft.com/en-us/dotnet/standard/parallel-programming/task-based-asynchronous-programming) objects. Methods that contain the [async](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/async) keyword can use [await](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/await) to wait for the operation to complete:

```csharp
async void AsyncMethod(StorageOperation operation)
{
    await operation;
    Debug.Log(operation);
}
```

### **Coroutines**

Coroutines can also use [yield return](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/statements/yield) to wait for the operation to complete:

```csharp
IEnumerator Coroutine(StorageOperation operation)
{
    yield return operation;
    Debug.Log(operation);
}
```

### **Callbacks**

It is also possible to execute a function when the operation completes successfully or fails, using [OnSuccess](https://unityapi.coherence.io/docs/v2.1.0/api/Coherence.Cloud.StorageOperation.OnSuccess.html#Coherence_Cloud_StorageOperation_OnSuccess_System_Action_) and [OnFail](https://unityapi.coherence.io/docs/v2.1.0/api/Coherence.Cloud.StorageOperation.OnFail.html#Coherence_Cloud_StorageOperation_OnFail_System_Action_Coherence_Cloud_StorageError__) respectively.

```csharp
void Method(StorageOperation<string> operation)
{
    operation.OnSuccess(result => Debug.Log($"Result: {result}"))
             .OnFail(error => Debug.LogWarning(error));
}
```

{% hint style="info" %}
If you need further info on how to do asynchronous programming within Unity, check out their [documentation](https://docs.unity3d.com/Manual/Coroutines.html).
{% endhint %}

### **Error Handling**

{% hint style="warning" %}
StorageOperations never cause [exceptions](https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/exceptions/) to be thrown, even if you use the `await` or `yield` keywords with them. Instead, it is expected that you will detect and handle errors **manually**.
{% endhint %}

You can determine if a completed StorageOperation has failed using the [HasFailed](https://unityapi.coherence.io/docs/v2.1.0/api/Coherence.Cloud.CloudOperation-1.HasFailed.html#Coherence_Cloud_CloudOperation_1_HasFailed) property. If an operation has failed, you can acquire more information about the failure via the [StorageError](https://unityapi.coherence.io/docs/v2.1.0/api/Coherence.Cloud.StorageError.html) object found in the [Error](https://unityapi.coherence.io/docs/v2.1.0/api/Coherence.Cloud.CloudOperation-1.Error.html#Coherence_Cloud_CloudOperation_1_Error) property.

```csharp
async void Start()
{
    var operation = await cloudStorage.LoadObjectAsync<string[]>(objectId, destroyCancellationToken);
    if(operation.HasFailed)
    {
        Debug.LogWarning(operation.Error);
        return;
    }
    
    if(operation.IsCanceled)
    {
        Debug.LogWarning("Operation was canceled");
        return;
    }
    
    foreach(string item in operation.Result)
    {
        Debug.Log($"Loaded item: \"{item}\".");
    }
}
```

## Serialization

When an object is saved to cloud storage, its state is serialized to JSON using [Json.NET](https://www.newtonsoft.com/json).

By default all the public properties and fields of objects are serialized.

You can use Json.NET's [serialization attributes](https://www.newtonsoft.com/json/help/html/SerializationAttributes.htm) to control how Json.NET serializes and deserializes your object.

### Supported Types

In addition to supporting all the [types that Json.NET can handle by default](https://www.newtonsoft.com/json/help/html/SerializationGuide.htm) (primitive types, arrays, lists, dictionaries...), Cloud Storage also supports the following Unity types:

* [Vector2](https://docs.unity3d.com/ScriptReference/Vector2.html)
* [Vector3](https://docs.unity3d.com/ScriptReference/Vector3.html)
* [Quaternion](https://docs.unity3d.com/ScriptReference/Quaternion.html)
* [Color](https://docs.unity3d.com/ScriptReference/Color.html)
* [Color32](https://docs.unity3d.com/ScriptReference/Color32.html)

Saving and loading objects whose type derives from [UnityEngine.Object](https://docs.unity3d.com/ScriptReference/Object.html) is not supported. You can however serialize a UnityEngine.Object into a string using the built-in [JsonUtility](https://docs.unity3d.com/ScriptReference/JsonUtility.html), and then save that string in cloud storage instead.

## Throttling

{% hint style="warning" %}
coherence Cloud has a rate limit of one API request of a particular type per second.
{% endhint %}

Having a rate limit of one API request of a type per second means is that if, for example, a save operation is made while another save operation is already in progress, then the second save operation will be queued, and will only get sent to the backend after one second has passed since the previous save operation.

However, the Cloud Storage service is able to **automatically batch multiple storage operations** into a single API request behind-the-scenes, which means that in practice an operation should almost never have to sit in queue for more than a second.

## Other Considerations

Currently, data is held for **1 hour**. In the future, this will be configurable.

Any client can read or write storage objects at any time. Data is available as long as the client knows the identifier to access it.
