# 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](https://unityapi.coherence.io/docs/v1.5.0/api/Coherence.Cloud.User.UserId.html#Coherence_Cloud_User_UserId) of a user that has logged in to coherence Cloud:

```csharp
StorageObjectId playerInventoryId = ("PlayerInventory", user.UserId);
```

The `StorageObjectId` type is serializable by Unity, and can also be edited using the Inspector as a serialized field.

```csharp
public StorageObjectId objectId = ("Level", Guid.NewGuid());
```

## Usage

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

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

### **Saving Objects**

The [SaveObjectAsync](https://unityapi.coherence.io/docs/v1.5.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 SaveExample : MonoBehaviour
{
    public StorageObjectId objectId = ("Greeter", 1);
    public CoherenceBridge bridge = null!;

    CloudStorage CloudStorage => bridge.CloudService.GameServices.CloudStorage;

    async void Start()
    {
        // Save the string "Hello, World!" in cloud storage:
        await CloudStorage.SaveObjectAsync(objectId, "Hello, World!");
    }
}
```

### **Loading Objects**

The [LoadObjectAsync](https://unityapi.coherence.io/docs/v1.5.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
async void Start()
{
    // Load a string from cloud storage:
    string text = await CloudStorage.LoadObjectAsync(objectId);
    Debug.Log(text);
}
```

### **Deleting Objects**

The [DeleteObjectAsync](https://unityapi.coherence.io/docs/v1.5.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
async void Start()
{
    // 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/v1.5.0/api/Coherence.Cloud.StorageOperation.html) or a [StorageOperation\<TResult>](https://unityapi.coherence.io/docs/v1.5.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/v1.5.0/api/Coherence.Cloud.StorageOperation.OnSuccess.html#Coherence_Cloud_StorageOperation_OnSuccess_System_Action_) and [OnFail](https://unityapi.coherence.io/docs/v1.5.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/6000.0/Documentation/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/v1.5.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/v1.5.0/api/Coherence.Cloud.StorageError.html) object found in the [Error](https://unityapi.coherence.io/docs/v1.5.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);
    if(operation.HasFailed)
    {
        Debug.LogWarning(operation.Error);
        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/6000.0/Documentation/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 **one 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.
