Sign In Register

How can we help you today?

Start a new topic
Answered

Social Friends System

For many reasons, developers choose to not integrate the various social networking integration we offer. In doing this the pass on the chance to leverage a really great system of managing friends. However, you can create your very own Social Network with GameSparks, allowing you much greater control over how your users interact.


In this guide we will be building a simple friends system much like Facebook

 

The flow will be as follows:

 

1. User registers, we assign them a friends list with their playerId

2. Users will can add friends, for this we will be using CreateChallengeRequest

3. Users can either Accept or Decline Friend Requests

4. Users will want to see their friends lists

 

We should first create our friend team. In the Configurator, make a new team, here's an example:

 



Next we are going to a create a player directory, this will be a custom Runtime Collection containing a list of every player's Id and Display Name.

 

Find RegistrationResponse in Cloud Code -> Responses and add the following:

  

var userName = Spark.getPlayer().getDisplayName();
var userId = Spark.getPlayer().getPlayerId();

//We are going to have a collection of all players so we can search it easily and return userIds
var playerCollection = Spark.runtimeCollection("playerDirectory");

//Insert a new document containing the playerId and playerName, this way we can search by name and return an Id we can use
playerCollection.insert({"playerId" : userId, "playerName" : userName});

//This is a good time to create the user's unique team, we'll force their teamId to be the same as their playerId + "friends"

 var success1 = Spark.sendRequest({
     "@class": ".CreateTeamRequest",
     "teamId": Spark.getPlayer().getPlayerId() + "friends",
     "teamType": "friendsList",
     "teamName": "Friends List"
    });

  

By setting their team up like this, it creates a good pattern for finding a user's team in the future.

 

Next we are going to create our challenges:


 

We'll also create a new ScriptMessageExtension for our FriendRequestMessage: 

 


 

Create new ScriptMessageExtension for the following also:

FriendRequestAcceptedMessage

 

In our Cloud Code we are going to write some code to change what would normally happen when a CreateChallengeRequest is fired.

 

Find CreateChallengeResponse in Cloud Code -> Responses and add the following:

  

if(Spark.getData().challengeInstanceId !== null){

//Detect if this is actually a friendRequest
    if(Spark.getData().shortCode === "friendRequest"){
    	//Store the name of the person sending the request
        thisChallenge.setScriptData("ChallengerName", Spark.loadPlayer(thisChallenge.getChallengerId()).getDisplayName())
    }
}

 

We will want to suppress the default ChallengeIssuedMessage from firing, it contains a lot of information we wont be using. find ChallengeIssuedMessage in Cloud Code -> GlobalMessages and add the following:

 

 

//Check if the challenge that spawned this messaged is equal to friendRequest
if(Spark.getData().challenge.shortCode == "friendRequest"){


    //Load the challenge
    var challenge = Spark.getData().challenge;
    //Get the requester's display Name
    var player = Spark.loadPlayer(Spark.getData().challenge.challenger.id);

    //Send a message to a challenged player with custom data they can use to reference the challenge/friendRequest again
    Spark.sendMessageExt({"message" : player.getDisplayName() +" wants to add you to their friendlist.", 
    "friendRequestId": challenge.challengeId,
    "senderId": player.getPlayerId()}, 
    "FriendRequestMessage", //This is the ScriptMessageExtension we set up before
    Spark.loadPlayer(challenge.challenged[0].id)); //This last line the the playerwe are going to send it to, in this case, the user we challenged
    
    //Prevent the ChallengeIssuedMessage from firing
    Spark.setScriptError("dontshow", "no point in showing it as we've sent another");
}
  

 

Next we'll write our Accept and Decline Friend request events.

 

Create an event called "AcceptFriendRequest" and add an attribute called "friendRequestId", this will be the challengeInstanceId of our friendRequest:

 

 

Make another event called "DeclineFriendRequest" and give it the same attribute.

 

In the Cloud Code for AcceptFriendRequest add the following:

  

//The friendRequestId is the challengeInstanceId of the friendRequest
var friendRequestId = Spark.data.friendRequestId;

//Load the friend request
var friendRequest = Spark.getChallenge(friendRequestId);

//Load the player accepting the request
var player = Spark.getPlayer();
//get the player's Username from their profile
var username = player.getDisplayName();

//Have the player sending the accept request add the challenge to their friends list
var success1 = Spark.sendRequestAs({
    "@class": ".JoinTeamRequest", 
    "teamId": friendRequest.getChallengerId()+"friends", 
    "teamType": "friendsList"}, 
    Spark.getPlayer().getPlayerId());
    
    
//Have the challenger add the player accepting the friend request to their friendsList
var success2 = Spark.sendRequestAs({
    "@class": ".JoinTeamRequest", 
    "teamId": Spark.getPlayer().getPlayerId()+"friends", 
    "teamType": "friendsList"}, 
    friendRequest.getChallengerId());
    
//Since the player accepting the friend request knows they've added the "challenger", 
//only send a message to the challenger that the friend request has been successful
    Spark.sendMessageExt({"message" : username +" has accepted your friend request!.", 
    "friendRequestId": friendRequestId,
    "senderId": player.getPlayerId()}, 
    "FriendRequestAcceptedMessage",
    Spark.loadPlayer(friendRequest.getChallengerId()));

//Since we want to prevent this being registered as a challenge, 
//we need to withdraw the challenge so it doesn't end some time in the future
Spark.sendRequestAs({"@class" : ".WithdrawChallengeRequest", "challengeInstanceId" : friendRequestId}, friendRequest.getChallengerId());

  

We'll also suppress this ChallengeWithdrawnMessage from sending, go to Global Messages -> ChallengeWithdrawnMessage and add the following: 

  

//We don't want the player to ever know the friend request was a challenge so we check and prevent the ChallengeWithdrawn from firing
if(Spark.getData().challenge.shortCode == "friendRequest"){
    Spark.setScriptError("dontshow", "no point in showing it withdrawn")
}

  

And in the DeclineFriendRequest Cloud Code add the Following: 

  

//Like the Accept Friend Request this is actually our challengeInstanceId
var friendRequestId = Spark.data.friendRequestId;

//Load the challenge
var friendRequest = Spark.getChallenge(friendRequestId);

//We are sending this as a DeclineChallengeRequest which will close the challenge
//Usually social sites dont inform the player if someone rejected them
var declineResult = Spark.sendRequestAs({"@class" : ".DeclineChallengeRequest", "challengeInstanceId" : friendRequestId}, friendRequest.getChallengedPlayerIds()[0]);

Spark.setScriptData("Declined", declineResult);

  

Many social networks wont inform a user if a player has declined their friend request, so to be true to the Facebook system, lets also suppress the ChallengeDeclinedMessage 

 

if(Spark.getData().challenge.shortCode == "friendRequest"){
    Spark.setScriptError("dontshow", "no point in showing it withdrawn")
}

 

Lastly, we will need some way to search for players so we can use their Id to start the whole process, for this we will create a "Find Player" event with these attributes:

 


 

In the cloud code for this event add the following:

 

  

//Load our playerDirectory collection
var playerCollection = Spark.runtimeCollection("playerDirectory");
//List all players who's display names even 
var playerList = playerCollection.find({playerName : ************ + Spark.getData().displayName}}).limit(Spark.data.entryCount).skip(Spark.data.offset);

//The array we'll store our player objects
var players = [];
    
//While there is another document in the collection
while(playerList.hasNext()){
    var player = playerList.next();
    //Remove the unnecessary document Id
    delete player["_id"];
    //Add the player's details to the array
    players.push(player);
}

//Set the response scriptData to return all documents found with the same or similar displayName as the one we entered
Spark.setScriptData("player", players);

  

 

To test this functionality we can open two tabs and register a new user in each.

 

So I have registered Shane and Hugo, the first thing we'll do is search for our potential friend:


Hugo sends and receives:

 

{
 "@class": ".LogEventRequest",
 "eventKey": "findPlayer",
 "displayName": "Shane",
 "entryCount": "10",
 "offset": "0"
}

{
 "@class": ".LogEventResponse",
 "scriptData": {
  "player": [
   {
    "playerId": "54edc940e4b08771a2ed730a",
    "playerName": "Shane"
   }
  ]
 }
}

 

 

With this information Hugo can initiate our friend request:

 

{
 "@class": ".CreateChallengeRequest",
 "accessType": "PRIVATE",
 "challengeShortCode": "friendRequest",
 "endTime": "2015-12-31T12:00Z",
 "usersToChallenge": "54edc940e4b08771a2ed730a"
}

{
 "@class": ".CreateChallengeResponse",
 "challengeInstanceId": "54edce95e4b08771a2ed75fd", // This is our friendRequest Id
 "scriptData": null
}

 

 

Shane Receives:

 

{
 "@class": ".ScriptMessage",
 "messageId": "54edce97e4b08771a2ed7603",
 "notification": true,
 "summary": "player + wants to add you to their friendlist.",
 "data": {
  "message": "Hugo wants to add you to their friendlist.",
  "friendRequestId": "54edce95e4b08771a2ed75fd",
  "senderId": "54edcd8be4b08771a2ed7551"
 },
 "extCode": "FriendRequestMessage",
 "playerId": "54edc940e4b08771a2ed730a"
}

 

 

Now Shane will accept the invite:

 

 

{
 "@class": ".LogEventRequest",
 "eventKey": "acceptFriendRequest",
 "friendRequestId": "54edce95e4b08771a2ed75fd"
}

{
 "@class": ".LogEventResponse",
 "scriptData": null
}

 

 

Hugo receives:

 

 

{
 "@class": ".ScriptMessage",
 "messageId": "54edcf39e4b08771a2ed766e",
 "notification": true,
 "summary": "player + accepted your friendRequest",
 "data": {
  "message": "Shane has accepted your friend request!.",
  "friendRequestId": "54edce95e4b08771a2ed75fd",
  "senderId": "54edc940e4b08771a2ed730a"
 },
 "extCode": "FriendRequestAcceptedMessage",
 "playerId": "54edcd8be4b08771a2ed7551"
}

 

We can now list our friend lists using GetTeamRequest:

 

{
 "@class": ".GetTeamRequest",
 "teamType": "friendsList"
}

{
 "@class": ".GetTeamResponse",
 "scriptData": null,
 "owner": {
  "id": "54edcd8be4b08771a2ed7551",
  "displayName": "Hugo",
  "online": true
 },
 "teamId": "54edcd8be4b08771a2ed7551friends",
 "teamType": "friendsList",
 "members": [
  {
   "id": "54edcd8be4b08771a2ed7551",
   "displayName": "Hugo",
   "online": true
  },
  {
   "id": "54edc940e4b08771a2ed730a",
   "displayName": "Shane",
   "online": true
  }
 ]
}

 

 



Best Answer

Hi Ahmet,


You first have to do a RegistrationRequest in order to launch this Cloud Code in the RegistrationResponse.


 

var userName = Spark.getPlayer().getDisplayName();
var userId = Spark.getPlayer().getPlayerId();
 
//We are going to have a collection of all players so we can search it easily and return userIds
var playerCollection = Spark.runtimeCollection("playerDirectory");
 
//Insert a new document containing the playerId and playerName, this way we can search by name and return an Id we can use
playerCollection.insert({"playerId" : userId, "playerName" : userName});
 
//This is a good time to create the user's unique team, we'll force their teamId to be the same as their playerId + "friends"
 
 var success1 = Spark.sendRequest({
     "@class": ".CreateTeamRequest",
     "teamId": Spark.getPlayer().getPlayerId() + "friends",
     "teamType": "friendsList",
     "teamName": "Friends List"
    });

 This is what will create your "playerDirectory" which from you will find your players. At every single registration, the player and his IDs will be added to the collection "PlayerDirectory" if not already inside.


You probably get an empty "player" tab with your response because you try to get information from this "playerDirectory" which remains empty because you don't call the RegistrationRequest.


You can also put this cloud code into AuthentificationResponse instead of RegistrationResponse if you are using DeviceAuthentification etc...


Hope this helps !



Hello, I have done everything as the example shows but I get the following result always. I checked many times example and my code are same

{

 "@class": ".LogEventRequest",

 "eventKey": "findPlayer",

 "displayName": "Test User 1",

 "entryCount": "10",

 "offset": "0"

}

 

{

 "@class": ".LogEventResponse",

 "scriptData": {

  "player": [  ]

 }

}

Answer

Hi Ahmet,


You first have to do a RegistrationRequest in order to launch this Cloud Code in the RegistrationResponse.


 

var userName = Spark.getPlayer().getDisplayName();
var userId = Spark.getPlayer().getPlayerId();
 
//We are going to have a collection of all players so we can search it easily and return userIds
var playerCollection = Spark.runtimeCollection("playerDirectory");
 
//Insert a new document containing the playerId and playerName, this way we can search by name and return an Id we can use
playerCollection.insert({"playerId" : userId, "playerName" : userName});
 
//This is a good time to create the user's unique team, we'll force their teamId to be the same as their playerId + "friends"
 
 var success1 = Spark.sendRequest({
     "@class": ".CreateTeamRequest",
     "teamId": Spark.getPlayer().getPlayerId() + "friends",
     "teamType": "friendsList",
     "teamName": "Friends List"
    });

 This is what will create your "playerDirectory" which from you will find your players. At every single registration, the player and his IDs will be added to the collection "PlayerDirectory" if not already inside.


You probably get an empty "player" tab with your response because you try to get information from this "playerDirectory" which remains empty because you don't call the RegistrationRequest.


You can also put this cloud code into AuthentificationResponse instead of RegistrationResponse if you are using DeviceAuthentification etc...


Hope this helps !


The current solution brought by the customer service doesn't implement friend suppression.


Here is my how to do it:


-Create a new Event called "removeFriend".

-Add an attribute called "friendPlayerId", string type, used in script.

-Go to Cloud Code and add this code to the "RemoveFriend" event:

 

var friendPlayerId = Spark.getData().friendPlayerId;
 
// Send a request as the current player (you), to leave your friend team.
var success1 = Spark.sendRequestAs({
    "@class": ".LeaveTeamRequest", 
    "teamId": friendPlayerId+"friends",
    "ownerId": friendPlayerId,
    "teamType": "friendsList"}, 
    Spark.getPlayer().getPlayerId());
     
     
// Send a request as your friend, to leave your team
var success2 = Spark.sendRequestAs({
    "@class": ".LeaveTeamRequest", 
    "teamId": Spark.getPlayer().getPlayerId()+"friends",
    "ownerId": Spark.getPlayer().getPlayerId(),
    "teamType": "friendsList"}, 
    friendPlayerId);

 Get your custom SDK and then, to use this request in your game, you can do as below:


 

public void DeleteFriend(string friendId)
    {
        new LogEventRequest_removeFriend().Set_friendPlayerId(friendId).Send((response) =>
            {
                if(!response.HasErrors)
                {
                    Debug.Log("Friend removed !");
                }
                else
                {
                    Debug.LogWarning("An error as occured.");
                }
            });
    }

 





1 person likes this

I found this guide very useful and I have now implemented it in our Unity game. It seems to work fine in most regards, but I have an issue about accepting friend requests. When I send a friend request from one user to another through the test harness, I can see the message popping up - when logging in on the user who received the request - that someone sent a friend request. It's no problem accepting that request through the test harness, because I have the challenge ID, but I don't know how to get all the challenge requests in Unity, so I can accept or decline them.


I assumed that FindChallengeRequest would be the method to use to get the pending friend requests, but no matter what parameters I pass to this method, it never returns a challenge. I want to be able to display a list of friend requests from within our Unity game and then have the user accept and decline. How do I go about doing that with this approach?

I figured it out. For others who might have the same issue, I can say that you need to look at messages. The friend requests are sent as messages so you need to do a ListMessageRequest and then look for friend requests within the received messages. The messages holds 'friendRequestId', which is the one you want to accept or reject. I don't know if this is the correct solution or the easiest way to do it, but it works for me.

However I did encounter one issue with the MessageList that is supplied with the ListMessageResponse as it seems to never hold any data? I managed to find the data I need by parsing the response's JSON string in Unity, but it would be easier to handle through the MessageList I imagine. But again, I've got it working, so it's not a very big deal.

Having a hard time calling "FindPlayer" event in UnrealEngine, editor crashes every time I call it. I must not be passing the data into the event properly, can anyone show me how they would pass the data into the log event in blueprints?


Currently I'm trying this: 


create log event attributes > set string > set number > set number > passed into the event data


With each field corresponding with displayName, entryCount, and offset. Is there something I am missing?

Is there any way I could check for pending friend requests? This is to avoid friend invite spamming. My case is I'm loading friends from Facebook and the user has the option to invite the selected friend into the GAME friends. Wanted to know if there is a way to check for pending requests in order to filter the Facebook friend list. 


I'm doing this in Unity btw, thanks


Regards,

Shiwen


How can we use a social leaderboard, when we use "Social Player System"? Will SocialDataRequest return the scores of a player's "Social Player System" friends or only game friends that are regiistered with a social media account?

Hi Seclan,


When using this setup and you have added friends to the friendList (team) they will be considered game friends and will show up in a ListGameFreindRequest along with social friends. With the Social toggle being set to on the members of this team will be regarded as friends of the friends of the owner. A SocialLeaderboardDataRequest will return the entries for the current player and their friends on the given leaderboard.


Thanks,

Liam

What if you want to have the player who send the FriendRequest see it's request in it's friendlist as Pending?

Because at the moment he has no indication that he have sent a friend request and only the one who got request have.

I was just thinking. It is making more sense to add those userIds to a RunTime Collection rather than making them part of the Team. I believe team's existence is more than just storing userIds and it's confusing as well. Also if I want to add one or more field with userId to store info like if it's a facebook friend or not  runtime collection is more flexible.


Thoughts, please.


1 person likes this

If using this setup lets you use buil-in friends function like ListGameFriendRequest (and all others I assume) why in this tutorial  https://docs.gamesparks.com/tutorials/social-features/creating-custom-friends-lists.html they did not use a team?. This sounds much better to me or Im missing something?


2 people like this
Login to post a comment