Persisting Data (Data Store)

To save persistent data to a player's account, you can use the Data Store. Currently, the only available data store is the UserWorldDataStore.

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

Overview

The data store is a Dictionary<string, object>, where the key is the variable name and the object is one of the supported types:

  • int
  • bool
  • float
  • double
  • long
  • decimal
  • string
  • Vector2
  • Vector3
  • Vector4
  • Quaternion
  • Color
  • DateTime
  • int[]
  • bool[]
  • float[]
  • string[]
  • Dictionary<string, object> (nested dictionary)

Limitations

  • Variable names cannot be more than 64 characters long
  • 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 data store has a maximum size limit of 1 MB
  • The value of the variable must be one of the supported types listed above

Usage

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

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("Got Saved 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("Got Saved Player High Score: " + playerHighScore);
});
}

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

Rate Limiting

The data store 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, it's important that you use a local copy to read/write the majority of your game state. You should not write to the data store every time a saved variable changes, instead you should manage saving your entire game state at intervals.

Below is an example where a variable timeSurvived is set every frame. We can't write this directly to the data store because we would be rate limited, so instead we write the value to a field and save to the data store 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 data store 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 data store at infrequent intervals
public void SaveTimeSurvived()
{
SpatialBridge.userWorldDataStoreService.SetVariable("timeSurvived", timeSurvived);
}

Nested Dictionaries

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

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

// Save a nested dictionary to the data store
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 data store
SpatialBridge.userWorldDataStoreService.SetVariable("savedata", saveData);
}

// Reads from the nested dictionary in the data store
public void LoadNestedData()
{
SpatialBridge.userWorldDataStoreService.GetVariable("savedata/player/name", "").SetCompletedEvent((response) => {
Debug.Log("Player name: " + response.stringValue);
});
}
// Save a nested dictionary to the data store
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 data store
SpatialBridge.userWorldDataStoreService.SetVariable("savedata", saveData);
}

// Reads from the nested dictionary in the data store
public void LoadNestedData()
{
SpatialBridge.userWorldDataStoreService.GetVariable("savedata/player/name", "").SetCompletedEvent((response) => {
Debug.Log("Player name: " + response.stringValue);
});
}