Creating Custom Friends Lists Using the Game Data Service

In this tutorial, we're going to build a very simple system for creating your own friends lists so your players can search for friends, send their friends invites, and get information about their current friends for use in Challenges or chat later on in your game.

Setting Up Indexing for Player List

We'll be creating a new custom playerList Data Type using Cloud Code and we'll want to query this using several indexed fields. As a first step, we need to add these indexed fields we want to use to query our Data Type.

1. Go to the Configurator>Gama Data page and create a new playerList.

2. Add the following five indexed fields, which we'll use later to query the playerList Date Type when we've added data to it:

Creating a Player List

Before we can start searching for players to add to our list we need to be able to get a Data Type of player information we can query so that we can filter our search requests. In this example, we'll be storing some of the important player information in this player list so we can search for user-names, countries, and so on. Therfore to start, we'll create a new player-doc each time a player registers with GameSparks and we'll do this in the RegistrationResponse script.

1. Go to Configurator>Cloud Code.

2. In the Scripts panel, select the Responses folder.

3. Open RegistrationResponse and add the following Cloud Code:

For the moment, we'll only put the player’s ID, user name, and display name in this script. In the next section, we'll add data to this so users can update it:

if(Spark.getData().error === undefined)
{
  //Load player
  var player = Spark.getPlayer();

  //Get Game Service Data API
  var API = Spark.getGameDataService();

  //Create new entry
  var entry = API.createItem("playerList", player.getPlayerId());

  //If valid, increment or initialise item
  var data = entry.getData();

  //set data
  data.userName = player.getUserName();
  data.displayName = player.getDisplayName();

  //Persist doc
  var status = entry.persistor().persist().error();

  //Check if Doc has been persisted without error
  if(status){
      Spark.setScriptError("ERROR", status);
      Spark.exit();
  } else{
      //Create playerFriends entry for player
      entry = API.createItem("playerFriends", Spark.getPlayer().getPlayerId());

      //Persist
      status = entry.persistor().persist().error();

      //If error, stop execution
      if(status){
        Spark.setScriptError("ERROR", status);
        Spark.exit();    
      }
  }
}

Testing for Player List

To test this, we can go to the Test Harness and register a new player:

When the player has been registered, we can see a new entry for this player in the playerList Data Type in the Data Explorer tab. Select the playerList Data Type, enter the playerId and click Find to see the player's document in this Data Type:

Adding More Information to Players

Next, we'll create a request so that we can add information to our player-documents. Others can then use those details to search for us.

In this example, we'll add some Attributes like country, city, and level to the Event. However, you can add other Attributes you think your players might want to search for.

To create a new Event:

1. Go to Configurattor>Events.

2. Click the Add button.

In this example, we'll call the Event updatePlayerInfo because we're not only going add information here, but we can also update existing information (like display name or user-name).

Player's ID is not Updated! Note that we'll never edit the player’s ID. This is our link back to the player’s GameSparks account and it's important that the playerId always remains the same.

Notice that we've given each of these Attributes a Default Value. This allows you to leave out the Attributes you don’t want to update. For instance, if you later wish to only update the value for the username Attribute, then you can just use that Attribute in the request. We’ll see an example of this later.

Now that the updatePlayerInfo Event has been created, we need to open it up in the Cloud Code editor and start updating the player docs:

3. Go to Configurator>Cloud Code.

4. In the Scripts panel, select the Events folder.

5. Open updatePlayerInfo and add the following Cloud Code:

Here's the script:

//Load player
var player = Spark.getPlayer();

//Get Game Service Data API
var API = Spark.getGameDataService();

//Attempt to retrieve entry
var entry = API.getItem("playerList", player.getPlayerId());

//Check for errors in returning document
if(entry.error()){
    Spark.setScriptError("ERROR", error);
    Spark.exit();
} else{
    //Get Data Object
    var data = entry.document().getData();

    //Check for input then update data object accordingly
    if(Spark.getData().userName != "null")
    {
        data.userName = Spark.getData().userName;
    }

    if(Spark.getData().displayName != "null")
    {
        data.displayName = Spark.getData().displayName;
    }

    if(Spark.getData().country != "null")
    {
        data.country = Spark.getData().country;
    }

    if(Spark.getData().city != "null")
    {
        data.city = Spark.getData().city;
    }

    if(Spark.getData().level != -1)
    {
        data.level = Spark.getData().level;
    }   
    //Attempt to persist document and retain any errors
    var status = entry.document().persistor().persist().error();
    //If there's an error in persisting
    if(status){
        Spark.setScriptError("ERROR", status);
        Spark.exit();
    }
}

Testing for Adding Information

We can test this through the Test-Harness. For this example, we'll only add info about my country, city, and level, excluding userName and displayName. You'll notice that the code above will ignore these fields because their values are null, while it will update everything else:

Now, if we go to the Data Explorer we'll see those fields updated in the player’s doc:

So now that players can be searched for and we can load players using the playerId, we are ready to make our search Events.

Searching for Friends

We need to create an Event which will allow us to search for available players that we can send friend requests to. We'll create the findPlayers Event for this, which will have an Attribute for every field we want to query for. These inputs will be compounded to create a query we'll search with.

In the script that we attach to this Event, we're going to add some code to find all the players matching the query and return them. Later on, we'll use the playerIds from this request to send friend invites:

//Get Game Service Data API
var API = Spark.getGameDataService();
//Forward declare condition
var condition;
//Forward declare player list which we will populate with entries based on our query
var playerList = [];

//Check for input and change query based on input
if(Spark.getData().userName != "null")
{
    var userNameCondition = API.S("userName").eq(Spark.getData().userName);
    condition = addCondition(condition, userNameCondition);
}

if(Spark.getData().displayName != "null")
{
    var displayNameCondition = API.S("displayName").eq(Spark.getData().displayName);
    condition = addCondition(condition, displayNameCondition);
}

if(Spark.getData().country != "null")
{
    var countryCondition = API.S("country").eq(Spark.getData().country);
    condition = addCondition(condition, countryCondition);
}

if(Spark.getData().city != "null")
{
    var cityCondition = API.S("city").eq(Spark.getData().city);
    condition = addCondition(condition, cityCondition);
}

if(Spark.getData().level != -1)
{
    var levelCondition = API.N("level").eq(Spark.getData().level)
    condition = addCondition(condition, levelCondition);
}   

//Exclude this player
condition.and(API.S("userName").ne(Spark.getPlayer().getUserName()));

//Run query
var entryList = API.queryItems("playerList", condition);

//If query has errors
if(entryList.error()){
     Spark.setScriptError("ERROR", entryList.error());
     Spark.exit();    
} else{
    //If no erros and entries
    var temp;
    var OBJ;
    //Loop through entries
    while (entryList.cursor().hasNext()){
        //Get reference of entry
        temp = entryList.cursor().next();
        //Populate data and ID of entry
        OBJ = temp.getData();
        OBJ.Id = temp.getId()
        //Add to list of players array
        playerList.push(OBJ);     
    }
    //Return list
    Spark.setScriptData("playerList", playerList)
}

//Function to dynamically create a compound query
function addCondition(condition, otherCondition) {
    if (condition) {
        return condition.and(otherCondition);
    }
    return otherCondition;
}

Note here that:

Testing for Searching Friends

At the moment, we only have one player registered, so we're going to have to register a few more players and add information to their documents in order for this to work properly. Once you have some new players registered, we can test this out in the Test Harness. The query field we're going to use here is just the field we want to check and the value we want to search for.

For example, if we know our friend’s userName or displayName, then we put that in directly:

This will also work for level or city. Just play around with the query to find out how you might want to use it. You could also add more complexity to this request, such as returning a limited number of players or filtering out the player data for privacy reasons.

Creating Friend Requests

So now that we can get a list of players, it’s time to invite those players to our list of friends. We're going to use another Data Type for this called playerFriends. The process of inviting will have two steps:

  1. We need to check to see if the friend has declined a previous request. We'll track this in the playerFriends Data Type.
  2. We send a message to the player and create a log in playerFriends to track that we’ve a pending friend request with that player.

So we'll need to create a new ScriptMessage and a new Event.

The message is going to be very simple. It’ll have a default title and message, and we’ll use the Short Code to send it from our Event.

To create a new ScriptMessage:

1. Go to the Configurator>Messages and select the ScriptMessage Extensions tab.

2. Click Add.

3. In the Add Message page, configure a new friendRequestMessage:

4. Next, we need to create the friendRequest Event. We'll add 3 Attributes to this Event:

Notice that we've entered Default Values for the group and message Attributes. This is because we shouldn’t require these to send a friend request. We can leave these out and just send the basic template of the message.

Cloud Code

We've discussed what the Cloud Code for this request is going to look like earlier, so the request will appear as follows:

Here's the full script:

//Get input
var playerId = Spark.getPlayer().getPlayerId();
var friendPlayerId = Spark.getData().player_id;
var group = Spark.getData().group;
var message = Spark.getData().message;

//Get Game Service Data API
var API = Spark.getGameDataService();

//Get the other player's friend's list
var entry = API.getItem("playerFriends", playerId);
// now we check the pending requests to see if the player has declined a previous request //
if(entry.error()){
    Spark.setScriptError("ERROR", error);
    Spark.exit();
} else{
    //Get Data object
    var data = entry.document().getData();
    //See if there's already a record between the two players
    var request = data[friendPlayerId];
    //If there is a record
    if(request){
        //Check if it's a declined request return message, however if it's not that means the request is already accepted or pending
        if(request.status === "declined"){
            Spark.setScriptError("ERROR", "player-declined");        
        } else{
           Spark.setScriptError("ERROR", "Request already made")      
        }
    } else{
        //If not declined create a pending request
    data[friendPlayerId] = {
        "displayName" : Spark.loadPlayer(friendPlayerId).getDisplayName(),
        "group" : group,
        "status" : "pending"    
    }

    //Persist entry
    var status = entry.document().persistor().persist().error();

    //Check for errors
    if(status){
        Spark.setScriptError("ERROR", status);
        Spark.exit();    
    } else{
        //If no errors send a message to the other player with the request
        var newMessage = Spark.message("friendRequestMessage");
        newMessage.setPlayerIds(friendPlayerId);
        newMessage.setMessageData({
        "message" : message,
        "senderId" : playerId,
        "displayName" : Spark.getPlayer().getDisplayName()

        });
        newMessage.send();
        }    
        //Get friends table of requests
        var entry = API.getItem("playerFriends", friendPlayerId);
        if(entry.error()){
            Spark.setScriptError("ERROR", error);
            Spark.exit();    
        } else{
            //Get data object and insert new request for reference
            var friendData = entry.document().getData();
            //Create a reference to this player's friend request
            friendData[playerId] = {
            "displayName" : Spark.getPlayer().getDisplayName(),
            "group" : "none",
            "status" : "pending"
            }
            //Persist data and ensure it was saved
            var status = entry.document().persistor().persist().error();
            if(status){
              Spark.setScriptError("ERROR", status);
                Spark.exit();   
            }
        }
    }
    // and return a script message to show that the request was completed successfully //
    Spark.setScriptData("success", "request-sent")
}

Testing for Creating Friends

To test this out, we need to log in two players through the Test Harness. You can get the player_id for the player you want to send the request to using the findPlayers request. Once you have the player selected, fill in the details of the friendRequest:

Once you send the request:

Accepting and Declining Requests

Accepting Requests

Accepting the friends request is pretty straightforward. We simply need to create a new acceptFriendRequest Event which will take the senderId from the request we just sent to the other player. They'll use this Event to accept the friend request:

In the Cloud Code for this Event, we want to check that the senderId is valid, and, if so, we'll update the document in the playerFriends Data Type to say that the request has been accepted. This friend’s details will then show up in the player's list of friends (which we'll create an Event for later).

We also want to send a message to the player who sent the friend request to tell them that their request has been accepted. In this example, we created another ScriptMessage with a simple template. There's no need to add extra data to this message because it just needs to say the request was accepted. If you need to add extra data, you can do so in the way that we did with the previous message:

Here's the script:

var friendId = Spark.getData().userId;

//Get Game Service Data API
var API = Spark.getGameDataService();

//Change player's entry
getEntryAndUpdate(Spark.getPlayer().getPlayerId(),friendId,true);
//Change friend's entry
getEntryAndUpdate(friendId,Spark.getPlayer().getPlayerId(),false);

function getEntryAndUpdate(ID, friendId,owner){
//Get the player's friend's list
var entry = API.getItem("playerFriends", ID);

//If error attempting to retrieve entry
if(entry.error()){
    Spark.setScriptError("ERROR", error);
    Spark.exit();
} else{
    //Get the data object of the entry
    var data = entry.document().getData();
    //Assuming a request was made and the object exists, change the 'status' variale to accepted
    var test = data[friendId];
    if(data[friendId] != null){
        data[friendId].status = "accepted";
    } else{
        Spark.setScriptError("ERROR", "No valid entry");
        Spark.exit();     
    }

    //Persist data
   var status = entry.document().persistor().persist().error();

   //If error attempting to persist
   if(status){
        Spark.setScriptError("ERROR", status);
        Spark.exit();    
   } else{
       if(owner){
            var newMessage = Spark.message("friendAcceptedMessage");
            newMessage.setPlayerIds(friendId);
            newMessage.send();   
       }

   }
}    
}


Testing for Accepting Requests

Now that we have this request created, we can log back into the Test Harness and get the senderId for the friend request we sent in the previous section:

Declining Friend Requests

Declining a friend request works in a similar way as accepting a friend request. We'll create a new declineFriendRequest Event for it, which will take the senderId. We also need to create a new ScriptMessage, so we have a template for declined request messages:

The Cloud Code for this Event is identical to the accepted request, except that we set the status of the request to “declined” or "You declined" so the decliner can send friend requests in the future and send out the friendRequestDeclined message instead:

Here's the script:

//Get Input
var friendId = Spark.getData().userId;

//Get Game Service Data API
var API = Spark.getGameDataService();

//Change player's entry
getEntryAndUpdate(Spark.getPlayer().getPlayerId(),friendId,true);
//Change friend's entry
getEntryAndUpdate(friendId,Spark.getPlayer().getPlayerId(),false);

function getEntryAndUpdate(ID, friendId,owner){
//Get the player's friend's list
var entry = API.getItem("playerFriends", ID);

//If error attempting to retrieve entry
if(entry.error()){
    Spark.setScriptError("ERROR", error);
    Spark.exit();
} else{
    //Get the data object of the entry
    var data = entry.document().getData();

    //Assuming a request was made and the object exists, change the 'status' variale to 'declined'
    //If owner we want to set the status as You Declined so the decliner can send a request in the future
    //But the declined player wont be able to
    if(data[friendId]){
        if(owner){
            data[friendId].status = "You Declined";
        } else{
            data[friendId].status = "declined";    
        }


    } else{
        Spark.setScriptError("ERROR", "No valid entry");
        Spark.exit();
    }

    //Persist data
   var status = entry.document().persistor().persist().error();

   //If error attempting to persist
   if(status){
        Spark.setScriptError("ERROR", status);
        Spark.exit();    
   }  else{
       if(owner){
            var newMessage = Spark.message("friendRequestDeclined");
            newMessage.setPlayerIds(friendId);
            newMessage.send();   
       }

   }
}    
}

Testing for Declining Requests

We can use the same setup as we did for accepting requests to test declining requests. All we need is a senderId we want to decline, which we used in friend request in the previous section:

You can also test that this player cannot send any more requests because our friendRequest Event checks to see if there are any declined requests before allowing the player to send new requests:

Getting a Player's Friends List

Next, we need to create a way for players to see a list of all their friends. So we'll create an Event called getPlayerFriends. This Event is going to have an Attribute for selecting which group we want to search for, which will default to all if we want to return all friends:

The Cloud Code for this Event will be very simple. We'll just query the playerFriends Data Type for friends matching the group requested and return all the friends we find. In this example, we only need to return the playerId and displayName, so we'll filter the results to only include this data:

Here's the script:

//Get inpuit data
var group = Spark.getData().group;
//Load Game Service API
var API = Spark.getGameDataService();
//Get player entry
var entry = API.getItem("playerFriends", Spark.getPlayer().getPlayerId());
//Forward declare player list object
var friendsList = {};

//Check for errors when returning player list entry
if(entry.error()){
    Spark.setScriptError("ERROR", error);
    Spark.exit();    
} else{
    //Data
    var data = entry.document().getData();

    //If input is not specific, return all friends
    if(group === "all"){
        for(var friendOBJ in data){
            //Set details of player ID and display name in new friendsList object
            friendsList[friendOBJ] = {};
            friendsList[friendOBJ].displayName = data[friendOBJ].displayName;
        }
    }
    else
    {
        //If group specific, return that group only
        //For every player in our friend list object
        for(var friendOBJ in data){
            if(data[friendOBJ].group === group && data[friendOBJ].status === "accepted"){
                //Set details of player ID and display name in new friendsList object
                friendsList[friendOBJ] = {};
                friendsList[friendOBJ].displayName = data[friendOBJ].displayName;
            }
        }
    }
//Return list to player
Spark.setScriptData("friendsList", friendsList);    
}

Testing for Getting Player's Friends List

We can test this Event using the Test Harness, as we’ve done before. If we use the Default Value of all for the group Attribute, we should see all of your friends returned:

For the following example, we’ve added a group Attribute for family into the getPlayerFriends requests, so we can also test that this returns specific groups:

Player Online Message

The last thing we are going to do is implement a system for notifying players when their friends come online. This is going to require a new ScriptMessage and in this case we'll use a system-script instead of creating a new Event.

GameSparks already has a script that runs when the player comes online which we can use in this case.

To create a new ScriptMessage:

1. Go to the Configurator>Messages and select the ScriptMessage Extensions tab.

2. Click Add.

3. In the Add Message page, configure a new friendOnLineMessage:

4. Click to Save and Close the new Script Message.

5. Go to Configurator>Cloud Code.

6. In the Scripts panel, select the System folder.

7. Open PlayerConnected and add the following Cloud Code:

Here's the Cloud Code for PlayerConnected:

//Get Game Data Service API
var API = Spark.getGameDataService();
var entry = API.getItem("playerFriends", Spark.getPlayer().getPlayerId());

//Ensure no error attemping to get friend's list table
if(entry.error()){
    Spark.setScriptError("ERROR", error);
    Spark.exit();    
} else{
    //Data
    var data = entry.document().getData();

    //For all players in the friend's list
    for(var friendId in data){
        var player = Spark.loadPlayer(friendId)
        //If they're online
        if(player.isOnline()){
            // send friend online message
            var newMessage = Spark.message("friendOnlineMessage");
            newMessage.setPlayerIds(friendId);
            newMessage.setMessageData({
            "senderId" : Spark.getPlayer().getPlayerId(),
            "displayName" : Spark.getPlayer().getDisplayName()
        });
        newMessage.send();
        }    
    }
}

Testing for Player Online Message

To test this, all we need is to login through the Test Harness with two players.

To show that the message is being correctly sent:

1. Authenticate a first player and keep them logged on.

2. Authenticate a second player - you should see that the first player gets a message through the Test Harness if the first player is on the second player's friends list:

Additional Options or Features

This tutorial implements a very basic set of features to send and receive friend requests and to get a player's list of friends. There's wide scope for additional functionality. For example, you can:

The important thing is that, once you have the friends list, you have access to the playerId of any of you friends. You can then use this playerId throughout the GameSparks APIs to set up Events, Matches, Challenges, or Teams to include your friends.

Try it out for yourself and contact our Customer Support or the forums if you need any help in implementing specific features regarding your players' friends in GameSparks!

Did this page help you? Please enter your feedback below. For questions about using this part of the platform, please contact support here