Skip to content

Teams, Clans and Guilds

In recent years, teams and clans have become a common feature of many top mobile titles. By allowing players to team up with others and share a common goal or purpose, as well as encouraging sense of responsibility towards other team members, teams and clans can significantly increase engagement and player investment in your game.

This tutorial shows how to implement a teams system in ChilliConnect using Cloud Code and Collection Data. This tutorial will cover:

  • Create a Cloud Data Collection in ChilliConnect to store teams and players
  • Using Player Data to store the current team the player is a member of
  • Creating Cloud Code Scripts to create, join and leave teams
  • Integrating the team system with the Unity SDK

The full Unity project for this tutorial is available on the ChilliConnect GitHub samples repository under the folder "Teams". To run the project under your own ChilliConnect game you should change the GAME_TOKEN constant in TeamsDemoSceneController.cs.

 Download Sample Code

Creating the Collections

Collection Data in ChilliConnect is a flexible cloud based data store that is ideal for sharing data between your players. We'll use a collection to store a list of teams in our game, with each collection object storing the team name and a list of players that are part of that team.

Go to the ChilliConnect dashboard, select your game and then select the Collection Data menu option. From here press the Add Collection button to create a new Collection, specifying "Teams" as the Name and TEAMS as the key. For the teams collection, we'll specify the a Name, and a list of Players that are part of each team. For each player, we will store their ChilliConnectID. In an actual game, you would also likely store a readable identifier such as DisplayName or UserName. We'll also define the Team Name field as a unique index to speed up searches and ensure no two teams can have the same name.

For this tutorial, we're going to modify teams through Cloud Code exclusively. To prevent modification directly from the game client, select the permissions tab, and change the API Write permission to "None".

You can read more about Collection fields and permissions in the Collection Data guide.

Creating Teams

We'll add a new Cloud Code Script to create new teams. From the ChilliConnect dashboard, select the Cloud Code menu option. From here press the Add Script button to create a new Script, accepting a single, required Name parameter:

Copy and paste the below Cloud Code in to your script:

try {

var sdk = ChilliConnect.getSdk("2.0.0");
/**
 * Check if player is part of an existing team
 */
var playerData = sdk.CloudData.getPlayerData(["TEAM"]);
if ( playerData.Values.length !== 0 ) {
    return {
        "Error" : true,
        "ErrorMessage" : "Player already assigned to team: " + playerData.Values[0].Value.TeamName
    }
}

var playerDetails = sdk.PlayerAccounts.getPlayerDetails();

/**
 * Create the new team object, taking the team name from the request
 * and adding the calling player as the only team member.
 */
var team = {
    "Name" : ChilliConnect.Request.Name,
    "Players" : [ 
        { "ChilliConnectID" :  playerDetails.ChilliConnectID }
    ]
};

/**
 * Add the team to the collection
 */ 
var result = sdk.CloudData.addCollectionObject( "TEAMS", team );

/**
 * Store the players current team against their player data
 */
sdk.CloudData.setPlayerData("TEAM", { 
    "TeamID" : result.ObjectID, 
    "TeamName" : ChilliConnect.Request.Name } );

}
catch( e)
{
    ChilliConnect.Logger.error(e.toString());
    return { 
        "Error": true, 
        "ErrorMessage" : "Unkown Error" 
    };
}

/**
 * Return the ObjectID of the new team
 */ 
return { 
    "Error" : false,
    "ErrorMessage" : "",
    "Team" : { 
        "TeamID" : result.ObjectID,
        "Name" : ChilliConnect.Request.Name,
        "Players" : team.Players
    }
};

This script checks the player is not currently a member of a team, accepts the team name from the request, creates a new team object with a single player and then returns the details of the created Object back to the game. The players new team is also stored under their Player Data. You can test the script from the "Test" tab of the Cloud Code view:

Running the script should return an output similar to below in the Response panel:

{
 "Output": {
  "Error": false,
  "ErrorMessage": "",
  "Team": {
   "TeamID": "85739e66-f542-11e6-9cb1-0a3391dcf55d",
   "Name": "SuperTeam",
   "Players": [
    {
     "ChilliConnectID": "uVfBzk9vi8ULx8Q2jP75mbkzRWNOjhNF",
     "UserName": null,
     "DisplayName": null,
     "Email": null
    }
   ]
  }
 }
}

We can also then view the TEAM Collection from Dashboard and see the created team object:

The script can be executed from a game using the Cloud Code Run Script method. Remember that scripts need to be published before they can be called from the ChilliConnect SDKs. In the sample, project TeamsSystem.cs is responsible for constructing the request, executing the script and parsing the script output:

public void CreateTeam (string teamName)
{
    var scriptParams = new Dictionary<string, SdkCore.MultiTypeValue> ();
    scriptParams.Add ("Name", teamName);

    var runScriptRequest = new RunScriptRequestDesc(SCRIPT_CREATE_TEAM);
    runScriptRequest.Params = scriptParams;

    m_chilliConnect.CloudCode.RunScript( runScriptRequest, 
        (request, response) => TeamCreatedCallBack(response.Output.AsDictionary()),
        (request, error) => Debug.LogError(error.ErrorDescription) );
}

private void TeamCreatedCallBack(MultiTypeDictionary output)
{
    //Check if the script returned an error as part of the response
    var wasError = output.GetBool("Error");
    if ( wasError ) {
        UnityEngine.Debug.Log("Error creating team");
        return;
    }

    //Get the Team data returned from the script
    var teamProperties = output.GetDictionary("Team");

    //Construct a new team object
    var team = new Team();
    team.ID = teamProperties.GetString("TeamID");
    team.Name = teamProperties.GetString("Name");
    team.PlayerCount = 1;

    //Set this as the players current team
    PlayerTeam = team;

    //Add to the local list of stored teams
    Teams.Insert(0, team);

    //Inform listeners that a new team is created, the players current team
    //has changed and that the team list has been updated
    OnTeamCreated(team);
    OnTeamsRefreshed(Teams);
    OnPlayerTeamRefreshed(PlayerTeam);
}

See Full Code 

Listing Teams

As we configured the Collection to be readable by all players, we don't need to create a Cloud Script to list teams in our game, we can just use the QueryCollection method directly. In the sample project, the FetchTeams() method in TeamsSystem.cs is used to query the collection to fetch available teams:

public void FetchTeams()
{
    var desc = new QueryCollectionRequestDesc(TEAMS_KEY);

    m_chilliConnect.CloudData.QueryCollection( desc, 
        (request, response ) => OnTeamsFetched(response) , 
        (request, error) => Debug.LogError(error.ErrorDescription));
}

private void OnTeamsFetched(QueryCollectionResponse response) 
{
    Teams.Clear ();
    foreach( CollectionDataObject teamObject in response.Objects) {
        var teamProperties = teamObject.Value.AsDictionary ();

        var team = new Team ();
        team.ID = teamObject.ObjectId;
        team.Name = teamProperties.GetString ("Name");
        team.PlayerCount = teamProperties.GetList ("Players").Count;
        Teams.Add( team );
    }

    OnTeamsRefreshed (Teams);
}

See Full Code 

We could also filter the list of returned teams by specifying a Query string. For example, to return all teams that were created after a specific date, we could set the query property in the sample above to:

desc.Query = "DateCreated > '2017-01-10'";

For more details on the type of queries that can be run against a Collection, see the full Collection Data user guide. Also note that the QueryCollection returns objects in pages of 20. Although not shown in this tutorial, you can also specify a Page parameter to return more teams up to a limit of 100 pages.

Joining Teams

To allow a player to join a team, we will create another Cloud Script. From the ChilliConnect dashboard, create a new script that accepts a single string, the TeamID that the player should join:

Paste the below code in to the script:

try {
var sdk = ChilliConnect.getSdk("2.0.0");

/**
 * Check if player is part of an existing team
 */
var playerData = sdk.CloudData.getPlayerData(["TEAM"]);
if ( playerData.Values.length !== 0 ) {
    return { "Error" : true, "ErrorMessage" : "Player already assigned to team: " + playerData.Values[0].Value.TeamName }
}

/**
 * Find the team specified by the TeamID.
 */
var matchedTeams = sdk.CloudData.getCollectionObjects("TEAMS", [ ChilliConnect.Request.TeamID ] );
if ( matchedTeams.Objects.length < 1 ) {
    return { "Error" : true, "ErrorMessage" : "Team not found with ID " + ChilliConnect.Request.TeamID };
}

var playerDetails = sdk.PlayerAccounts.getPlayerDetails();

/**
 * getCollectionObjects returns an array, get the first team (should only be 1 returned)
 */ 
var team = matchedTeams.Objects[0];

/**
 * Get the list of players - extracting the list of players in to a local
 * variable before modifying is important due to the way CloudCode works. 
 * See the Cloud Code documentation for more details:
 * 
 * https://docs.chilliconnect.com/guide/cloudcode/#modifying-arrays-returned-from-chilliconnect
 */
var players = team.Value.Players;

/**
 * Add the player to the team
 */ 
players.push( { "ChilliConnectID" : playerDetails.ChilliConnectID } );

/**
 * Update the object. Collection data supports partial updates, so we 
 * can provide just the "Players" parameter. Also define the WriteLock from 
 * the version of the team object we read - this ensures that if another 
 * player is attempting to join the team at the same time, then the update
 * will fail and their change won't be overwritten
 */ 
sdk.CloudData.updateCollectionObject( "TEAMS",  ChilliConnect.Request.TeamID, 
    { "Players" : players }, team.WriteLock );

/**
 * Store the players current team against their player data
 */
sdk.CloudData.setPlayerData("TEAM", { "TeamID" : ChilliConnect.Request.TeamID, 
    "TeamName" : team.Value.Name } );

}
catch( e)
{
    ChilliConnect.Logger.error(e.toString());
    return { "Error": true, "ErrorMessage" : "Unknown Error" };
}

return { "Error" : false, "ErrorMessage" : "" };

This script will update the Players list of the collection object to add the requesting player to the team. If the team cannot be found or the player is already a member of a team, an error will be thrown. When the player is added to the team, Team details are stored in a Player Data key to make it easy for the game to lookup the players current team.

This script does more work than the Create Team script and there are some important points that should be noted:

  • The GetCollectionObjects method is used to return the team object referenced in the request. This method should always be preferred over QueryCollection when loading a specific Object as QueryCollection operates in near real-time, where updates to Collection Objects may take up to one second to become visible. GetCollectionObjects on the other hand always returns the most recent version of the matched Object.

  • Before modifying the Players list, the array is read in to a local variable. This is required when modifying lists of values from Cloud Code due to the way that lists are loaded in to Code Scripts. See the See the Cloud Code documentation for more details

  • When updating the Team, the Cloud Code Script provides the WriteLock value from the existing version of the team Object. This is to prevent concurrent updates to the Team overwriting each others changes. In this example, the script will simply fail, but the specific error can also be caught and the updates re-attempted if required. See the Cloud Code documentation for more details.

Again this script can be tested from the "Test" tab in the dashboard. In the sample project, the JoinTeam method in TeamsSystem.cs is used to invoke the join team script.

public void JoinTeam (Team team)
{
    var scriptParams = new Dictionary<string, SdkCore.MultiTypeValue> ();
    scriptParams.Add ("TeamID", team.ID);

    var runScriptRequest = new RunScriptRequestDesc(SCRIPT_JOIN_TEAM);
    runScriptRequest.Params = scriptParams;

    m_chilliConnect.CloudCode.RunScript( runScriptRequest, 
        (request, response) => JoinTeamCallBack( response.Output.AsDictionary(), team ),
        (request, error) => Debug.LogError(error.ErrorDescription) );
}

private void JoinTeamCallBack(MultiTypeDictionary output, Team team)
{
    var wasError = output.GetBool("Error");
    if ( wasError ) {
        UnityEngine.Debug.Log("Error joining team");
    }
    else {
        PlayerTeam = team;
        OnPlayerTeamRefreshed(team);
    }
}

See Full Code 

Leaving Teams

Like joining and creating teams, we'll use a Cloud Code script to enable players to leave teams. Create a new script with no input parameters:

Paste in the below code to the script:

try {
var sdk = ChilliConnect.getSdk("2.0.0");
/**
 * Check if player is part of an existing team
 */
var playerData = sdk.CloudData.getPlayerData(["TEAM"]);
if ( playerData.Values.length != 1 ) {
    return { "Error" : false, "ErrorMessage" : "" };
}

var teamId = playerData.Values[0].Value.TeamID;

/**
 * Find the team specified by the TeamID from the player data
 */
var matchedTeams = sdk.CloudData.getCollectionObjects("TEAMS", [ teamId ]);
if ( matchedTeams.Objects.length < 1 ) {
    sdk.CloudData.deletePlayerData("TEAM");
    return { "Error" : false, "ErrorMessage" : "" };
}

/**
 * getCollectionObjects returns an array, get the first team (should only be 1 returned)
 */ 
var team = matchedTeams.Objects[0];

/**
 * Get the list of players - extracting the list of players in to a local
 * variable before modifying is important due to the way CloudCode works. 
 * See the Cloud Code documentation for more details:
 * 
 * https://docs.chilliconnect.com/guide/cloudcode/#modifying-arrays-returned-from-chilliconnect
 */
var players = team.Value.Players;

/**
 * Get ChilliConnectID of currently logged in player
 */ 
var loggedInPlayersId = sdk.PlayerAccounts.getPlayerDetails().ChilliConnectID;

/**
 * Remove the player from the list of playuers
 */ 
players = _.reject( players, function( item ) { return item.ChilliConnectID == loggedInPlayersId; } );

/**
 * Update the object.
 */ 
sdk.CloudData.updateCollectionObject( "TEAMS",  teamId, 
    { "Players" : players }, team.WriteLock );

/**
 * Clear saved player data
 */ 
sdk.CloudData.deletePlayerData("TEAM");

}
catch( e)
{
    ChilliConnect.Logger.error(e.toString());
    return { "Error": true, "ErrorMessage" : "Uknown Error" };
}


return { "Error" : false, "ErrorMessage" : "" };

The leave team script uses similar API methods as the previous scripts and is fairly self explanatory. After checking that the team exists the script will load the team object from the Collection, remove the player from the Players list and update the object. As with joining a team, the WriteLock value is used to prevent concurrent updates. The TEAM key that stores the players current team in their Player Data is also cleared.

The LeaveTeam() method in TeamsSystem.cs is used to invoke the script.

public void LeaveTeam()
{
    var runScriptRequest = new RunScriptRequestDesc(SCRIPT_LEAVE_TEAM);

    m_chilliConnect.CloudCode.RunScript( runScriptRequest, 
        (request, response) => LeaveTeamCallBack(response.Output.AsDictionary() ),
        (request, error) => Debug.LogError(error.ErrorDescription) );
}

private void LeaveTeamCallBack(MultiTypeDictionary output)
{
    var wasError = output.GetBool("Error");
    if ( wasError ) {
        UnityEngine.Debug.Log("Error leaving team");
    }
    else {
        PlayerTeam = null;
        OnPlayerTeamRefreshed(null);
    }
}

See Full Code 

Next Steps

This tutorial showed how Cloud Code and Collection Data can be use to create a simple teams or clans system in your game in just a few minutes. To expand on this example, you could also investigate:

  • Implement an invite system so players must request to join a team
  • Allow players to search for specific teams
  • In the Create Team Cloud Code script, create a "proxy" player account using CreatePlayer that can be used with the AsPlayer Cloud Code method to implement Team-specific versions of inventories, balances, messages and leaderboards.