CloudBuilder  2.8.4
Making social games is easy !
Working with user related data

When working on your new game, one of the first things you will want to do is saving some information about this user. By using Clan of the Cloud APIs for storing information, it will be available from any of the devices this user will use to log in with his Clan of the Cloud profile. You can ensure this way that your players will enjoy the same experience, and this also acts a backup in case a device is lost. Storing some user's information in the cloud is possible with Clan of the Cloud APIs of course, but you have many different ways to achieve this. We will detail below the possibilities that are available to the developer, and will explain when to use them, since some APIs are better suited depending on what you want to save.

C++ version

class MyGame {
void ReadUserProfile() {
}
void WriteUserProfile() {
json->Put("displayName", "My user display name");
json->Put("lang", "fr");
delete json;
}
void ReadUserProfileDone(CloudBuilder::eErrorCode aErrorCode, const CloudBuilder::CCloudResult *aResultGetProfile) {
if (aErrorCode == CloudBuilder::enNoErr)
printf("The profile display name is %s\n", aResultGetProfile->GetJSON()->GetString("displayName"));
else
printf("GetProfile failed with message %s!\n", aResultGetProfile->GetErrorString());
}
void WriteUserProfileDone(CloudBuilder::eErrorCode aErrorCode, const CloudBuilder::CCloudResult *aResultSetProfile) {
if (aErrorCode == CloudBuilder::enNoErr)
printf("Profile modified\n");
else
printf("SetProfile failed with message %s!\n", aResultSetProfile->GetErrorString());
}
};

Unity/C# version

class MyGame {
public void ReadUserProfile() {
CUser.GetProfile(delegate(eErrorCode aErrorCode, string aResultGetProfile) {
if(aErrorCode == eErrorCode.enNoErr) {
// From the result, we get the serialized JSON;
LitJson.JsonData json = LitJson.JsonMapper.ToObject(aResultGetProfile);
Debug.Log("The profile display name is " + (string) json["displayName"]);
}
else
Debug.Log("GetProfile failed with message " + aErrorCode);
});
}
public void WriteUserProfile() {
Dictionary<string, string> json = new Dictionary<string, string>();
json["displayName"] = "My user display name";
json["lang"] = "fr";
CUser.SetProfile(LitJson.JsonMapper.ToJson(json), delegate(eErrorCode aErrorCode, string aResultSetProfile) {
if(aErrorCode == eErrorCode.enNoErr)
Debug.Log("Profile modified");
else
Debug.Log("SetProfile failed with error " + aErrorCode);
});
}
}

The most flexible way to store any kind of information for your users is to use the key/value storing feature provided. Each user has a dedicated set of keys and values, that you are free to manage as you wish. There is no restriction, so you can at any time add, modify or remove any keys. It's entirely up to the developer to manage this storage, and Clan of the Cloud will not interfere with it.
Note that you have two options for using this feature. If you don't pass a domain when accessing your keys, then everything will be local to your game. However, if you pass a domain previously created from your 'CloudBuilder Manager' (https://account.clanofthecloud.com/#/share), then your whole set of keys and values will be accessible from all applications which share this domain.
This means first that you theoretically have an unlimited number of sets of keys and values. Then also, be sure when using domains, to make matching Read and Write calls in the same domain, or otherwise you will not be able to read some previously written data in another domain!
Also, if you ignore the "key" entry in your JSON configuration when writing some information, be aware that you are replacing all the existing keys for this user. This option should be used with great care, and only under precise circumstances, or you may lose all the data for your user! In the same way, but without the dramatic consequences, not passing "key" when reading information, you will be returned the whole set of keys associated to this user.
In the following samples, the information stored for a given key is a simple string. However, it could be a whole JSON if you need so. If this were the case, you'd have to adapt the read operation for such a key in order to handle the result as a JSON object, and not as a string.

C++ version

class MyGame {
void ReadDataFromKey(const char* aKey) {
// Not filling in the "domain" entry implicitly means we want to retrieve from the "private" domain.
json->Put("key", aKey);
delete json;
}
void WriteDataWithKey(const char* aKey, const char* aValue) {
// Not filling in the "domain" entry implicitly means we want to retrieve from the "private" domain.
json->Put("key", aKey);
json->Put("data", aValue);
delete json;
}
void ReadDataFromKeyDone(CloudBuilder::eErrorCode aErrorCode, const CloudBuilder::CCloudResult *aResultReadDataFromKey) {
if (aErrorCode == CloudBuilder::enNoErr)
printf("Value is %s\n", aResultReadDataFromKey->GetJSON()->GetString("value"));
else
printf("Read operation failed with message %s!\n", aResultReadDataFromKey->GetErrorString());
}
void WriteDataWithKeyDone(CloudBuilder::eErrorCode aErrorCode, const CloudBuilder::CCloudResult *aResultWriteDataWithKey) {
if (aErrorCode == CloudBuilder::enNoErr)
printf("Write operation succeeded\n");
else
printf("Write operation failed with message %s!\n", aResultWriteDataWithKey->GetErrorString());
}
};

Unity/C# version

class MyGame {
public void ReadDataFromKey(string aKey) {
Dictionary<string, object> json = new Dictionary<string, object>();
json["key"] = aKey;
CUser.KeyValueRead(LitJson.JsonMapper.ToJson(json), delegate(eErrorCode aErrorCode, string aResultReadDataFromKey) {
if(aErrorCode == eErrorCode.enNoErr) {
// From the result, we get the serialized JSON;
LitJson.JsonData json = LitJson.JsonMapper.ToObject(aResultReadDataFromKey);
Debug.Log("Value is " + (string) json["value"]);
}
else
Debug.Log("Read operation failed with error " + aErrorCode);
});
}
public void WriteDataWithKey(string aKey, string aValue) {
Dictionary<string, object> json = new Dictionary<string, object>();
json["key"] = aKey;
json["data"] = aValue;
CUser.KeyValueWrite(LitJson.JsonMapper.ToJson(json), delegate(eErrorCode aErrorCode, string aResultWriteDataWithKey) {
if(aErrorCode == eErrorCode.enNoErr)
Debug.Log("Write operation succeeded");
else
Debug.Log("Write operation failed with error " + aErrorCode);
});
}
}

There is another way to manage player's informations. For generic purposes and complex data, you should use Key/Value as described above. However, if you want to store very simple information like bool, string and number, you can use the User Properties APIs. Specifically, they are mandatory to use when you want to implement match making into your game. It's here that you will store properties which have to be matched against other players' properties so we can return you some possible opponents. But nothing prevents you from using it also for storing other bits of information, if they are trivial, which might never be used for match making (since in match making, it's up to the developer to build the query that will return possible opponents).
Just like with Key/Value APIs, User Properties belong to a domain. Not filling in the domain entry when working with properties means that you want to access it in the default, private domain of the game. If it is the case, then these properties will not be shared amongst different games.

C++ version

class MyGame {
void GetProperty(const char* aProperty) {
// Not filling in the "domain" entry implicitly means we want to retrieve from the "private" domain.
}
void SetProperty(const char* aKey, const char* aValue) {
// Not passing the domain parameter implicitly means we want to retrieve from the "private" domain.
json->Put(aKey, aValue);
delete json;
}
void GetPropertyDone(CloudBuilder::eErrorCode aErrorCode, const CloudBuilder::CCloudResult *aResultGetProperty)
{
if (aErrorCode == CloudBuilder::enNoErr) {
CHJSON* property = aResultGetProperty->GetJSON()->Get("properties");
printf("Property Level is %d\n", property->GetString("Level"));
}
else
printf("Property retrieval failed with message %s!\n", aResultGetProperty->GetErrorString());
}
void SetPropertyDone(CloudBuilder::eErrorCode aErrorCode, const CloudBuilder::CCloudResult *aResultSetProperty)
{
if (aErrorCode == CloudBuilder::enNoErr)
printf("SetProperty succeeded\n");
else
printf("SetProperty failed with message %s!\n", aResultSetProperty->GetErrorString());
}
};

Unity/C# version

class MyGame {
public void GetProperty(string aProperty) {
CUser.GetProperty(delegate(eErrorCode aErrorCode, string aResultGetProperty) {
if(aErrorCode == eErrorCode.enNoErr) {
// From the result, we get the serialized JSON;
LitJson.JsonData property = LitJson.JsonMapper.ToObject(aResultGetProperty)["properties"];
Debug.Log("Property Level is " + (string)property["Level"]);
}
else
Debug.Log("Property retrieval failed with error " + aErrorCode);
}, aProperty, "private");
}
public void SetProperty(string aProperty, string aValue) {
Dictionary<string, object> json = new Dictionary<string, object>();
json[aProperty] = aValue;
CUser.SetProperty(delegate(eErrorCode aErrorCode, string aResultSetProperty) {
if(aErrorCode == eErrorCode.enNoErr)
Debug.Log("SetProperty succeeded");
else
Debug.Log("SetProperty failed with error " + aErrorCode);
}, LitJson.JsonMapper.ToJson(json), "private");
}
}