Persisting Data (Data Store)

In Spatial to save persistent data to a players account you use what is called a DataStore. Currently the only available data store is the UserWorldDataStore.

Spatial does not support the use of PlayerPrefs or any save-data system besides the datastore.


Overview

The datastore is a Dictionary of type <string,object>, where the object can be any one of the supported types: int bool float double long decimal string Vector2 Vector3 Vector4 Quaternion Color DateTime int[] bool[] float[] string[] Dictionary<string, SUPPORTED_OBJECT_TYPE>

  • Max variable name length is 64
  • Variable names cannot start with a number
  • Variable names can only contain alphanumeric characters (a-z A-Z 0-9) and _
  • Variable names cannot start or end with _
  • The datastore has a maximum size limit of 1mB.

Usage

Below is an example of using the UserWorldDataStore service to save and load the "playerHighScore" value from the datastore.

public int playerHighScore = 0;

public void LoadHighScore()
{
SpatialBridge.userWorldDataStoreService.GetVariable("playerHighScore", 0).SetCompletedEvent((response) => {
//once the GetVariable request is completed this code will run.
playerHighScore = response.intValue;
Debug.Log("Player High Score: " + playerHighScore);
});
}

public void SaveHighScore()
{
SpatialBridge.userWorldDataStoreService.SetVariable("playerHighScore", playerHighScore);
}
public int playerHighScore = 0;

public void LoadHighScore()
{
SpatialBridge.userWorldDataStoreService.GetVariable("playerHighScore", 0).SetCompletedEvent((response) => {
//once the GetVariable request is completed this code will run.
playerHighScore = response.intValue;
Debug.Log("Player High Score: " + playerHighScore);
});
}

public void SaveHighScore()
{
SpatialBridge.userWorldDataStoreService.SetVariable("playerHighScore", playerHighScore);
}

Rate Limiting

The datastore is limited to 10 write operations per minute. Changes made in a single frame can be batched together and only count as a single write.

Due to this limit its important that you use a local copy to read/write the majority of your game state. You should not write to the datastore every time a saved variable changes, instead you should manage saving your entire game-state at intervals.

Bellow is an example where a variable timeSurvived is set every frame. We can't write this directly to the datastore because we would be rate limited, so instead we write to a local variable, and save to the datastore at infrequent intervals.

public float timeSurvived = 0f;

private void Update()
{
// we need to read/write to this every frame so we store it locally
timeSurvived += Time.deltaTime;
}

//save to datastore at infrequent intervals
public void SaveTimeSurvived()
{
SpatialBridge.userWorldDataStoreService.SetVariable("timeSurvived", timeSurvived);
}
public float timeSurvived = 0f;

private void Update()
{
// we need to read/write to this every frame so we store it locally
timeSurvived += Time.deltaTime;
}

//save to datastore at infrequent intervals
public void SaveTimeSurvived()
{
SpatialBridge.userWorldDataStoreService.SetVariable("timeSurvived", timeSurvived);
}

Nesting

Because Dictionary<string,object> is a supported value type for the datastore, this means you can store a nested dictionary in a single operation. Nested values can be accessed using a / separated key.

Bellow is an example of saving and reading a nested dictionary.

//save a nested dictionary to the datastore
public void SaveNestedData()
{
//create data
Dictionary<string, object> saveData = new Dictionary<string, object>();
saveData.Add("player", new Dictionary<string, object> { { "name", "player" }, { "score", 0 }, { "gold", 0 } });
saveData.Add("enemy", new Dictionary<string, object> { { "name", "enemy" }, { "score", 0 }, { "gold", 0 } });
//save to datastore
SpatialBridge.userWorldDataStoreService.SetVariable("savedata", saveData);
}

//get the saved player name
public void LoadNestedData()
{
SpatialBridge.userWorldDataStoreService.GetVariable("savedata/player/name", "").SetCompletedEvent((response) => {
Debug.Log("Player name: " + response.stringValue);
});
}
//save a nested dictionary to the datastore
public void SaveNestedData()
{
//create data
Dictionary<string, object> saveData = new Dictionary<string, object>();
saveData.Add("player", new Dictionary<string, object> { { "name", "player" }, { "score", 0 }, { "gold", 0 } });
saveData.Add("enemy", new Dictionary<string, object> { { "name", "enemy" }, { "score", 0 }, { "gold", 0 } });
//save to datastore
SpatialBridge.userWorldDataStoreService.SetVariable("savedata", saveData);
}

//get the saved player name
public void LoadNestedData()
{
SpatialBridge.userWorldDataStoreService.GetVariable("savedata/player/name", "").SetCompletedEvent((response) => {
Debug.Log("Player name: " + response.stringValue);
});
}