In order to access coherence Cloud features from the Unity SDK, you can use the CloudService instance in the CoherenceBridge component.
With the CloudService instance, and after completing login (see Worlds and Rooms example code for an example), you will be able to fetch the existing Worlds of your project, create new Rooms, delete and fetch existing Rooms, as well as access the various available Game Services.
In the subsections of this page, you will be able to find detailed usage examples of each feature from the CloudService API.
In the inspector of a instance, you will find two settings related with the coherence Cloud:
Auto Login As Guest: If enabled, Cloud Service will automatically log in with the coherence Cloud using a player Guest Account. If you want to handle logins manually in your game, turn this option off.
Cloud Unique ID: Uniquely identify the Cloud Service used by this CoherenceBridge. It will be used to cache the player account credentials. If it is not specified, one will be autogenerated.
In the subsections of this page you will find example usages of the GameServices API that can be found in CloudService.GameServices.
After creating a World in the Dashboard, you can join it from your Unity Project using the CloudService.Worlds API combined with the CoherenceBridge.
Here is an example:
using System.Collections.Generic;
using Coherence.Cloud;
using Coherence.Toolkit;
using UnityEngine;
public class AsyncWorldsExample : MonoBehaviour
{
public CoherenceBridge bridge;
private CloudService cloudService;
void Start()
{
cloudService = bridge.CloudService;
JoinFirstAvailableWorld();
}
private async void JoinFirstAvailableWorld()
{
// Wait for connection to be established with the coherence Cloud, authenticated as a Guest User.
await cloudService.WaitForCloudServiceLoginAsync(1000);
// Fetch all available worlds from our Project
IReadOnlyList<WorldData> availableWorlds = await cloudService.Worlds.FetchWorldsAsync();
// Join the first available world, if one exists
if (availableWorlds.Count > 0)
{
bridge.JoinWorld(availableWorlds[0]);
}
}
}After enabling at least one region in the Dashboard, you can use the CloudService.Rooms API to create Rooms and delete and fetch existing Rooms. After fetching an existing Room, you can join it via the CoherenceBridge.JoinRoom method:
using System.Collections.Generic;
using Coherence.Cloud;
using Coherence.Toolkit;
using System.Collections;
using UnityEngine;
public class AsyncRoomsExample : MonoBehaviour
{
public CoherenceBridge bridge;
private CloudService cloudService;
void Start()
{
cloudService = bridge.CloudService;
CreateAndJoinRoom();
}
private async void CreateAndJoinRoom()
{
// Wait for connection to be established with the coherence Cloud, authenticated as a Guest User.
await cloudService.WaitForCloudServiceLoginAsync(1000);
// Fetch all available Room regions from our Project
IReadOnlyList<string> availableRegions = await cloudService.Rooms.RefreshRegionsAsync();
// Create a room and join it for the first available region
if (availableRegions.Count > 0)
{
CloudRoomsService roomsService = cloudService.Rooms.GetRoomServiceForRegion(availableRegions[0]);
RoomData newRoom = await roomsService.CreateRoomAsync(RoomCreationOptions.Default);
bridge.JoinRoom(newRoom);
}
}
}The key-value store provides a simple persistence layer for the players.
The player needs need to be logged in to use the key-value store.
This class provides the methods to set, get and unset key-value pairs. This is executed within the context of the currently logged in player.
Size: there are no limits to the number of stored key/values as long as the total size is less than 256 kB.
Requests: Set/Get/Unset can be called unlimited amount of times but the execution may be throttled.
By default, the CloudService will automatically authenticate as a Guest User, unless you disable Auto Login As Guest option in the CoherenceBridge inspector.
If you disable the automatic login, you can handle Player authentication manually through the CloudService.GameServices.AuthService API.
In this example, you can see how you can manually login as a Guest:
using Coherence.Cloud;
using Coherence.Toolkit;
using System.Collections;
using UnityEngine;
using Result = Coherence.Runtime.Result;
public class LoginAsGuestExample : MonoBehaviour
{
public CoherenceBridge bridge;
private CloudService cloudService;
void Start()
{
cloudService = bridge.CloudService;
StartCoroutine(CheckRefreshLogin());
}
private IEnumerator CheckRefreshLogin()
{
IAuthClient authService = cloudService.GameServices.AuthService;
while (!cloudService.IsConnectedToCloud && authService.SessionTokenRefreshResult == null)
{
yield return null;
}
// If refresh failed, we have to login as a guest again
if (authService.SessionTokenRefreshResult == Result.InvalidCredentials)
{
var task = authService.LoginAsGuest();
while (!task.IsCompleted)
{
yield return null;
}
}
// At this point, we are ready to fully utilize the Cloud Service
}
}You can also choose to allow your users to create their own Player accounts, you can use the Authentication Service API in a similar fashion to allow for manual authentication:
using Coherence.Cloud;
using Coherence.Toolkit;
using System.Collections;
using UnityEngine;
using Result = Coherence.Runtime.Result;
public class KeyValueStoreExample : MonoBehaviour
{
public CoherenceBridge bridge;
public string username;
public string password;
public string myKey;
public string myValue;
private CloudService cloudService;
void Start()
{
cloudService = bridge.CloudService;
StartCoroutine(LoginAsAUserAndUseKvStore());
}
private IEnumerator LoginAsAUserAndUseKvStore()
{
IAuthClient authService = cloudService.GameServices.AuthService;
while (!cloudService.IsConnectedToCloud && authService.SessionTokenRefreshResult == null)
{
yield return null;
}
// If refresh failed, we have to login as a user with the given credentials again
if (authService.SessionTokenRefreshResult == Result.InvalidCredentials)
{
var task = authService.LoginWithPassword(username, password, true);
while (!task.IsCompleted)
{
yield return null;
}
}
// At this point, we are ready to fully utilize the Cloud Service
KvStoreClient keyValueStore = cloudService.GameServices.KvStoreService;
// Sets a value
// key: lowercase letters, numbers, underscore, dash
// val: any string (null is not allowed)
keyValueStore.Set(myKey, myValue);
// Gets a value
// key: lowercase letters, numbers, underscore, dash
var foo = keyValueStore.Get(myKey);
Debug.Log(string.Format("foo={0}", foo)); // foo=1
// Unsets a value, removing it from the store
// key: lowercase letters, numbers, underscore, dash
keyValueStore.Unset(myKey);
}
}using Coherence.Cloud;
using Coherence.Toolkit;
using System.Collections;
using UnityEngine;
using Result = Coherence.Runtime.Result;
public class LoginAsUserExample : MonoBehaviour
{
public CoherenceBridge bridge;
public string username;
public string password;
private CloudService cloudService;
void Start()
{
cloudService = bridge.CloudService;
StartCoroutine(LoginAsAUser());
}
private IEnumerator LoginAsAUser()
{
IAuthClient authService = cloudService.GameServices.AuthService;
while (!cloudService.IsConnectedToCloud && authService.SessionTokenRefreshResult == null)
{
yield return null;
}
// If refresh failed, we have to login as a user with the given credentials again
if (authService.SessionTokenRefreshResult == Result.InvalidCredentials)
{
var task = authService.LoginWithPassword(username, password, true);
while (!task.IsCompleted)
{
yield return null;
}
}
// At this point, we are ready to fully utilize the Cloud Service
}
}In order to be able to use the Lobbies feature, you must enable Persisted Player Accounts in the Game Services section of your Project dashboard settings.
Since Lobbies use Rooms under the hood for the Game Session, you must also have at least one region enabled in the Rooms section of your dashboard.
Accessible from CoherenceBridge.CloudService.CloudRooms, the Lobbies Service API has access to every endpoint you need to interface with the Lobbies feature.
Attributes are one of the main avenues available for customizing the Lobbies logic and fitting it to the needs of your game project.
Every Player in the Lobby and the Lobby itself can have custom Attributes associated to them.
An Attribute is defined by:
Key: Identifier for the Attribute.
Value: Value of the Attribute, can be either an integer number or a string.
Index: A predetermined value that will allow the Attribute to be used in Filter Expressions.
Aggregate: This only applies to the lobby indexes and can be: “sum”, “avg”, “min”, “max” or “owner” for the integer indexes or “owner” for the string indexes. When a player joins/leaves the lobby their indexes are aggregated into the lobby indexes.
Player or Lobby Attributes cannot be deleted, you can only add new ones or update existing ones.
If you wish to stop using an Attribute, we recommend setting a default value that you can interpret as such, like an empty string for a string Attribute, or a zero value for an integer Attribute.
Indexed Attributes are aggregated from the Player Attributes into a Lobby Attribute.
For example, let's say every Player in my Lobby has a Matchmaking integer rating value Attribute associated to him, and we index this Attribute to n1.
We want Players to be able to do Matchmaking with Lobbies using the average Matchmaking rating of all the players present in the Lobby. The Lobby will have a "Matchmaking rating average" integer Attribute associated with it, that is also indexed to n1, with an aggregator of type "avg" to do the average of every Player present in the Lobby.
Every time a Player joins or leaves the Lobby, the Lobby Attribute indexed to n1 will be updated accordingly.
When you're entering the Matchmaking phase, you can use a Lobby Filter Expression to search for specific Lobbies that match the current rating of the Player that is initiating the Matchmaking phase.
In order to start the Matchmaking process, you can use the FindOrCreateLobby method. Based on the given parameters to find a Lobby, the response of this method will be either an existing Lobby or a fresh new Lobby where your player will be the owner.
The bread and butter of the Matchmaking logic can be done through our powerful filter expression building system. The FindOrCreateLobby method accepts up to three different filters.
This is similar to the WHERE clause in an SQL-like expression. You can build filter expressions using Lobby Attribute indexes and most of the Lobby properties.
The player can use up to three filter expressions to filter out Lobbies. The expressions are tried in order until one of them succeeds. This can be used to increasingly expand the search criteria and can be very efficient if the amount of players is low.
Currently, these are the properties that you can use to customize your Matchmaking process:
Room Regions
Tags
Max Players
Number of Players
For example:
“region in [‘us’, ‘eu’] and (s1 = ‘map1’ or s1 = ‘map2’)”
In Unity, you will build this filter expression using the builder pattern, if we wanted to match using the expression from the example, we would do it like this:
When you successfully join a Lobby, an instance of type LobbySession will be returned via the Lobbies API.
You can use this LobbySession instance to interface with the Lobby itself, you will find methods to leave the Lobby, update your Player Attributes or send a message to other Players. You will also be able to query all the information about the Lobby itself, like every Player present, their Attributes, maximum Players and other Lobby properties.
Additionally, if you're the owner of the Lobby, you will have an instance of type LobbyOwnerActions available to you (for non-owners this instance will be null referenced). As an owner of the Lobby, you will be able to kick other Players, update the Attributes of the Lobby, or start the Game Session.
LobbySession instance will have a number of callbacks you can subscribe to that will inform you of any updates that happen to the Lobby, like a Player joining or leaving, or Attributes being updated.
If a Room associated with a Lobby is closed, Players will also receive a callback.
When the owner of the Lobby starts the Game Session, every Player will receive a callback with the Room endpoint data that can be used to connect to the Replication Server using the associated CoherenceBridge instance.
If you need to perform custom logic before actually joining the Room, you can subscribe a custom listener through the LobbiesService instance in CoherenceBridge.CloudService.Rooms.LobbyService.OnPlaySessionStarted. If no custom listener is registered, coherence will automatically connect to the Replication Server.
If the player has closed the game abruptly without leaving the Lobby, when the game is restarted and the Player authenticates with the coherence Cloud, in the Login response you will receive the ID of the Lobbies that the Player is currently a part of. You can use these Lobby IDs to fetch again the LobbySession instance from the Lobbies Service API.
Additionally, the Lobby might have a current Game Session active, so you can use the Room data that is available to reconnect to the Replication Server Room.
Here is an example of how to handle this scenario:
Public: Public Attributes will be returned and visible to all Players.
Available Slots
Simulator Slug
Is Private Lobby
Integer Attribute Index
String Attribute Index
using Coherence.Cloud;
using Coherence.Toolkit;
using UnityEngine;
using Coherence.Cloud.Coroutines;
using System.Collections;
using System.Collections.Generic;
public class ExampleClass : MonoBehaviour
{
public CoherenceBridge bridge;
void Start()
{
CoherenceBridgeStore.TryGetBridge(gameObject.scene, out bridge);
StartCoroutine(MatchmakingRoutine());
}
private IEnumerator MatchmakingRoutine()
{
CloudService cloudService = bridge.CloudService;
yield return cloudService.WaitForCloudServiceLoginRoutine();
var regionsYield = cloudService.Rooms.WaitForFetchRegions();
yield return regionsYield;
IReadOnlyList<string> regions = regionsYield.RequestResponse.Result;
// Build filter for regions and either map1 or map2
LobbyFilter lobbyFilter = new LobbyFilter()
.WithAnd()
.WithRegion(FilterOperator.Any, new List<string>(regions))
.WithOr()
.WithStringAttribute(FilterOperator.Equals, StringAttributeIndex.s1, "map1")
.WithStringAttribute(FilterOperator.Equals, StringAttributeIndex.s1, "map2")
.End();
// You can use the ToString method to debug what your filters look like
// This outputs: “region in [‘us’, ‘eu’] and (s1 = ‘map1’ or s1 = ‘map2’)”
Debug.Log($"{lobbyFilter.ToString()}");
FindLobbyOptions findOptions = new FindLobbyOptions()
{
Limit = 20,
LobbyFilters = new List<LobbyFilter>() { lobbyFilter }
};
// If we don't find a suitable Lobby, we will create one with Lobby Attribute map1 and the first available region
CloudAttribute mapAttribute = new CloudAttribute("map", "map1", StringAttributeIndex.s1, StringAggregator.None, true);
var createOptions = new CreateLobbyOptions()
{
LobbyAttributes = new List<CloudAttribute>() { mapAttribute },
Region = regions[0]
};
// We start the Matchmaking process with the coherence Cloud
cloudService.Rooms.LobbyService.FindOrCreateLobby(findOptions, createOptions, OnRequestFinished);
}
private void OnRequestFinished(RequestResponse<LobbySession> requestResponse)
{
if (requestResponse.Status == RequestStatus.Success)
{
// On successful request, we fetch the LobbySession instance from the response result
LobbySession myLobby = requestResponse.Result;
}
}
}using Coherence.Cloud;
using Coherence.Toolkit;
using UnityEngine;
using Coherence.Runtime;
public class LobbyLoginExample : MonoBehaviour
{
public CoherenceBridge bridge;
void Start()
{
CoherenceBridgeStore.TryGetBridge(gameObject.scene, out bridge);
IAuthClient authClient = bridge.CloudService.GameServices.AuthService;
// We subscribe to the On Login callback from the Authentication Service
authClient.OnLogin += AuthClientOnLogin;
}
private async void AuthClientOnLogin(LoginResponse loginResponse)
{
// We check the Lobby Ids returned by the Login Response object
if (loginResponse.LobbyIds == null || loginResponse.LobbyIds.Count == 0)
{
return;
}
// We fetch a LobbySession instance from the Lobby Service API
LobbySession lobbySession = await bridge.CloudService.Rooms.LobbyService.GetActiveLobbySessionForLobbyId(loginResponse.LobbyIds[0]);
if (lobbySession == null)
{
return;
}
// Found LobbySession, we check if there is a GameSession active by querying the RoomData attached to LobbyData
var hasRoomSession = lobbySession.LobbyData.RoomData.HasValue;
if (hasRoomSession)
{
// The Lobby has an ongoing Game Session so we reconnect to the Room using the CoherenceBridge
bridge.JoinRoom(lobbySession.LobbyData.RoomData.Value);
}
else
{
// The Lobby doesn't have an ongoing Game Session, here we can update our UI accordingly
}
}
}