Hi Rameez,
Actual files (e.g. saved to Cloud Save Files) can only be read and written by the players that own them.
Cloud Save Player Data, supports Access Classes, including Public Data, which other players can read from.
For example, Player A could write data like this:
using SaveOptions = Unity.Services.CloudSave.Models.Data.Player.SaveOptions;
public async void SavePublicData()
{
var data = new Dictionary<string, object> { { "keyName", "value" } };
await CloudSaveService.Instance.Data.Player.SaveAsync(data, new SaveOptions(new PublicWriteAccessClassOptions()));
}
And Player B could read it like this:
public async void LoadPublicDataByPlayerId()
{
var playerId = "JE1unrWOOzzbIwy3Nl60fRefuiVE";
var playerData = await CloudSaveService.Instance.Data.Player.LoadAsync(new HashSet<string>{"keyName"}, new LoadOptions(new PublicReadAccessClassOptions(playerId)));
if (playerData.TryGetValue("keyName", out var keyName)) {
Debug.Log($"keyName: {keyName.Value.GetAs<string>()}");
}
}
Of course Player B would have to know Player A’s Player ID in this example!
In practice, you would want to define an Index for the key (e.g. in the Unity Dashboard, under Cloud Save) so that when Player A writes to the specified key, other players are able to see it come up when they query for items for sale.
Using a Query from in game looks like this:
public async void QueryPlayerData()
{
var query = new Query(
// The first argument to Query is a list of one or more filters, all must evaluate to true for a result to be included
new List<FieldFilter>
{
new FieldFilter("indexedKeyName", "value", FieldFilter.OpOptions.EQ, true),
new FieldFilter("anotherIndexedKeyName", "otherValue", FieldFilter.OpOptions.NE, true),
},
// The second (optional) argument is a list of keys you want to be included with their values in the response
// This may include keys which are not part of the index, and does not need to include the index keys
// If you don't specify any, you will still get back a list of IDs for Players that matched the query
new HashSet<string>
{
"indexedKeyName"
}
);
var results = await CloudSaveService.Instance.Data.Player.QueryAsync(query);
Debug.Log($"Number of results from query: {results.Count}");
results.ForEach(r =>
{
Debug.Log($"Player ID: {r.Id}");
r.Data.ForEach(d => Debug.Log($"Key: {d.Key}, Value: {d.Value.GetAsString()}"));
});
}
You can find more examples like this in the Cloud Save SDK Tutorial.
Of course at some point (e.g. when they actually want to “purchase” a card from another player) you can going to need some server authoritative logic to handle the trading between players - either a game server or Cloud Code.
You can find an example of how to interact with Cloud Save from Cloud Code to read and write data across multiple players here:
https://docs.unity.com/ugs/en-us/manual/cloud-code/manual/modules/how-to-guides/cross-player-data#Update_top_5_player_data_using_Cloud_Save
Note: If you don’t want to change how you are currently saving and loading data, you can also use Cloud Code to read data from any player and return it to another player, some advantages of using Access Classes are that you don’t incur costs from running a cloud function every time you do a read operation, and that it will be a faster operation (as you are reading directly from Cloud Save) and ultimately less code may be required, but either approach is fine.