question

brendan avatar image
brendan asked

Two-way friend confirmation with Cloud Script

Brendan Vanous
started a topic on Wed, 12 August 2015 at 7:18 PM

Question from a developer:

We want to have a two-way friend system, such that players can request to be friends of each other, but a user is only added to the friend list of a player when that user accepts. Could we do this like so?

  • Player 1 selects to send a friend request to Player 2

  • The title stores this in an Entity Object or File, or Player Data for each player, pending for Player 1, requested for Player 2

  • Title uses Push Notification (via Cloud Script) and/or Photon Chat to send notification to the other player

  • Player 2 sees the Push, or the Chat, or finds the Friend request upon login (when the title checks his outstanding friend request data)

  • Player 2 accepts the friend request

  • Title adds players to each others' friend lists

Are there any problems with this flow?

1 comment
10 |1200

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

glen avatar image glen commented ·

I'd like to see this implemented as well. There is not a "vote" button for this post though as it resides in questions not feature requests. I feel like if this had been in feature requests we'd be looking at 4 or 5 votes on it already given the feedback in this post.

0 Likes 0 ·
brendan avatar image
brendan answered

Edit: Update go-live date for Server API calls (code landed, all tests passed).

While we do plan to provide a two-way friend system at a later date, here's the way to implement a two-way friends system for now:

First, note that if you want this to be fully secure, you'll need to disable the Client-side AddFriend, RemoveFriend, and SetFriendTags API calls using our API Permission Policies (https://playfab.com/blog/permission-policies/), and make those calls from Cloud Script.

Also, please note that the update which adds the SetFriendTags call to the Server API is expected to go live the week of February 5th, 2017.

  • Player 1 selects to send a friend request to Player 2
  • Use AddFriend on both players, setting each in the other player's friend list
  • Use SetFriendTags on both players, to tag the players uniquely ("requester"/"requestee", or similar)
  • Title uses Push Notification (via Cloud Script) and/or Photon Chat to send notification to the other player
  • Player 2 sees the Push, or the Chat, or finds the friend request upon login (using GetFriendsList, checking for the "requester" tag)
  • Player 2 accepts or declines the friend request
  • If Player 2 accepts, either remove the tags from both players or set them to something like "confirmed"
  • If Player 2 declines, remove each player from the other player's friend list

The obvious downside to this is that players who are still in a "pending" state will see each other in friend leaderboard results. However, as Shared Group Data is not sharded or cached, and so would have issues with many simultaneous requests to the same row in a data table (as would be the case if many users attempt to read the same SGS all at once), this implementation will be far more robust and reliable. Also, the Tags in the data returned by the GetFriendsList call will allow you to appropriately filter the users according to their real friend status.

16 comments
10 |1200

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

brendan avatar image brendan ♦♦ commented ·

The Server/SetFriendTags API call is now live in the service, and will be added to the documentation and SDKs when they are next updated (early next week). If you'd like to add it to your own projects now, it's an easy update - the call takes a PlayFabId for the user whose friends you are changing, the FriendPlayFabId of the user who should be tagged, and the Tags array (see the Client API version for an example of the FriendPlayFabId and Tags). And obviously, as a Server API call, it requires the Secret Key for authentication.

1 Like 1 ·
glen avatar image glen brendan ♦♦ commented ·

Awesome! Thanks for the update

0 Likes 0 ·
glen avatar image glen commented ·

Thanks for updating this with the proper solution. It can be very difficult to understand the best way to implement features using PlayFab. Perhaps this would be a good workflow to add to Recipes, Guides or Tutorials.

Will the referenced API permissions be added to the Game Manager or will they only be accessible via the Admin API?

0 Likes 0 ·
brendan avatar image brendan ♦♦ glen commented ·

We'll be adding the API permissions settings to the Game Manager in a future sprint, but we don't have a date for that just yet. For now, the Admin API calls would be the way to go.

0 Likes 0 ·
wowbigmac avatar image wowbigmac commented ·

To get the user tagged with "requestee" for example we need to use the GetPlayerTags method at the server API, am i correct ?

0 Likes 0 ·
brendan avatar image brendan ♦♦ wowbigmac commented ·

No, just use the GetFriendsList call. The point is that using this method, the players are in each others' friends lists, just tagged so that you know whether or not they are confirmed friends.

0 Likes 0 ·
wowbigmac avatar image wowbigmac brendan ♦♦ commented ·

I'm just having trouble retrieving the Pending's friend display name. how do i go on getting the players tagged with "Requestee"'s display name ?

0 Likes 0 ·
Show more comments
Show more comments
franciscoraposo avatar image franciscoraposo commented ·

Hi @Brendan!

You mention here a two-way friend system that would be implemented later. Is it available? If not, is there an ETA for it?

Thanks!

0 Likes 0 ·
brendan avatar image brendan ♦♦ franciscoraposo commented ·

Sorry, we don't have a date for that right now. As the needs of the live service (making sure everything is working well for all live titles) takes priority, we're not really able to lock down dates for features that aren't at the top of the priority list. As we are able to provide granularity on upcoming dates for new features, we will always post those to our blog (https://blog.playfab.com/blog).

0 Likes 0 ·
HackerTester avatar image HackerTester commented ·

Everybody Refer to this thread to get Accept/Decline Friend Function working using Cloudscripts (It Actually Work!) -----

https://community.playfab.com/questions/46961/add-friend-using-friend-request-with-acceptdecline.html

0 Likes 0 ·
brendan avatar image
brendan answered

Best Answer
Brendan Vanous said on Wed, 12 August 2015 at 7:18 PM

First off, we do plan to offer a two-way friend system in the service in future. We haven't had many requests for this, but if it's key to your title, please do let us know (we prioritize in part based upon how many titles are asking for features).

The flow described would work, though there is one thing worth noting:

The existing friend system in PlayFab is a follow-style system, meaning when someone says "add friend", it adds that player - no confirmation. And since the AddFriend call is made from the Client, a technically savvy user could see this and make the necessary calls to add other players to their friend list. Likely not a destructive action in your game, but something you should make sure to note in your planning.

Brendan


3 Comments
Brendan Vanous said on Wed, 12 August 2015 at 7:18 PM

First off, we do plan to offer a two-way friend system in the service in future. We haven't had many requests for this, but if it's key to your title, please do let us know (we prioritize in part based upon how many titles are asking for features).

The flow described would work, though there is one thing worth noting:

The existing friend system in PlayFab is a follow-style system, meaning when someone says "add friend", it adds that player - no confirmation. And since the AddFriend call is made from the Client, a technically savvy user could see this and make the necessary calls to add other players to their friend list. Likely not a destructive action in your game, but something you should make sure to note in your planning.

Brendan


duelante said on Wed, 12 August 2015 at 11:49 PM

Hi Brendan,

Thanks for your response. Good to hear your adding in the Invite system. I am surprised it is not a popular request. Do you have an ETD for this?

As we do not want to be constrained by people accepting push notifications or only seeing friend invites when they log in, we will use Photon chat as our P2P "push" system.

We are not too concerned by the small Invite hack as you mentioned but we will still explore some ways to prevent P2P interaction until a friend request has been accepted. However, if you're delivering the Invite system soon then we won't spend much time on this :)


Brendan Vanous said on Thu, 13 August 2015 at 12:13 AM

It's not scheduled right now - as I said, it hasn't been a common request, so it's not on our roadmap currently. For that reason, if you need it in the near-term, I'd recommend the method above. For the hack, there is one additional measure you can take: Don't attach any players to the Shared Group Data. If it's only write-able from Cloud Script, then a player can't add themselves to it. You can then use that as another check of the friend status of players, and make decisions based upon it (preferably in Cloud Script, so that a hacked client can't influence the results).

Brendan

10 |1200

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

Alberto Gomez avatar image
Alberto Gomez answered

I know it has been a year since this question came up, but I was wondering if this is ever going to be done. We need this for our game Clicker Pirates :)

10 |1200

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

brendan avatar image
brendan answered

As you can see from the votes, we still haven't had a lot of requests for this, so I'm afraid it has not bumped to the top of the priority list yet. We use the information on how many people need any given feature to help prioritize, so there are a number of other items which have had more requests ahead of this one, at the moment. For now, I would recommend the flow described in the post at the top of this thread as the way to implement this.

10 |1200

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

e.timofeev@flexilestudio.com avatar image
e.timofeev@flexilestudio.com answered

We need this for our game too.

1 comment
10 |1200

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

brendan avatar image brendan ♦♦ commented ·

Could you click the vote button for this feature, then? Right now, this feature still only has one vote. The "vote" button is "like" inside the thread, but "vote" when you're viewing it via search or any other thread listing, like the Home page.

0 Likes 0 ·
Joshua Strunk avatar image
Joshua Strunk answered

I have been thinking about this problem recently, and felt like posting my thoughts on a more detailed look at how any system implemented using Cloud Script and Shared Group Data would actually work.

*It is getting late I am going to have to come back to this post in the morning and clean it up but feel it is important to get it out there. (I switched to a weird psedo code half way through writing, sorry)

Scenerio:

Current addFriend is follow system without anyone actually visibly having any followers. The end goal of this is for whenever a user follows(addFreind) another user get the followers playfabID into a list on the followed's PlayerData.

Caveats

  • It is critical that the followers list not miss anyone who has called addFriend.
  • It is fine if there is a delay between following and and followed seeing there new follower in there list.(Think handle at next login)
  • It is also fine if a request to follow or unfollow fails, as long as failure results in both Followed and Follower sharing the same state in the relationship
  • Must handle cases for both follows and unfollows.

Notation

  • Follower: A single user who is attempting to add Followed to their "friends"/ follow them
  • Followed: A single user who is attempting to be be followed by Follower
  • Bucket: A single shared group data instance.
  • Relationship Change: Follow | Unfollow.
  • FollowerStatusUpdate Bucket: In this contex is just a general term for the current Shared Group Data Follower would write there relationship change request to follow Followed. This write would be a key:value where the key is the Follower playerID, and the value would in the most simple form would just be a string Follow or Unfollow.

Implementation Theory:

When Followed logs in we check and see if we have a valid FollowerStatusUpdate Bucket setup.

We do this by checking to see if we have a Player.ReadOnlyData key/value called followerStatusUpdateBucketId which holds a valid id for a Bucket.

If Followed does not have FollowerStatusUpdate Bucket setup we do that by creating a new Bucket and saving its id to Followed's followerStatusUpdateBucketID.

If Followed's FollowerStatusUpdate Bucket is setup we must process its contents. This is the bulk of the work in this theroy so will look at this later.

Now looking at how Follower follows Followed once Followed has a FollowerStatusUpdate Bucket setup.

https://gist.github.com/JoshuaStrunk/fb3ae84f210904d64dc3f1b9ea3109a0

Back to Followed process the contents of a bucket when they login

https://gist.github.com/JoshuaStrunk/4e66884421ca0e2527ba143808393716

Bonus Round

A major steamer starts streaming and popularity in the game surges (great!), but now all of these new followers want to follow the streamer in our game. The system must have some way of supporting a spike in followers to a single user >100(number of keys in shared data).

8 comments
10 |1200

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

brendan avatar image brendan ♦♦ commented ·

Unfortunately, there are a number of ways this will break, starting with multiple attempts to write the same information at the same time - so, users A and B both want to follow C, and so their writes to followerStatusUpdateBucketId stomp each other. And in the case where you have a ton of people trying to read or write the same non-cached/sharded data (Shared Group Data or User Data) at the same time like this, you're going to hit table read limits. And since that could impact multiple titles, we would have to consider whether we have to throttle your title at the service level until you can fix the title code to no longer do that.

Realistically, this two-way functionality would need to either wait for a solution designed for it in PlayFab, or else use an external data table.

0 Likes 0 ·
glen avatar image glen commented ·

Is it feasible to have a Shared Group per player as a way to hold friend requests for that player, but limit the number of keys? Say 50 or less? Then before allowing other players to write to that Shared Group you first check the key count, if greater than 50 then reject and don't write to that Shared Group. In this case there could be many read attempts to the same Shared Group but writes to it would be limited.

0 Likes 0 ·
Joshua Strunk avatar image Joshua Strunk glen commented ·

I would note even though my psedo code is horrible and I am going to re write it, I never read the SGD from the users following/unfollowerin. It is only ever read from the player processing the requests.

I was going to rely on the limit error preventing writes from going through and was going to tackle this issue at a later date.

0 Likes 0 ·
brendan avatar image brendan ♦♦ glen commented ·

Shared Group Data has a limit of 5 keys, by default. So no, I can't recommend using it for that purpose. And to be very transparent, that limit is not being hard-enforced yet but it will be in future, so I cannot recommend creating a dependency on the ability to write to more keys in a single SGD without a custom agreement with our team, as otherwise this could result in issues in the title when we are enforcing this (we will do our best to prevent any breaking issues for titles, but if there's an excessive number of keys per SGD, we may not be able to guarantee that). Shared Group Data was specifically introduced as a limited data store for a small number of people to share state information for asynchronous games - any use outside that should be thoroughly reviewed, to make sure it won't have issues.

0 Likes 0 ·
glen avatar image glen brendan ♦♦ commented ·

I see the 5 KVP limit per update in the limits tab of the Game Manager. But I don't see the 5 key limit for the entire Shared Group.

This is a bit of a problem for my title as we've implemented a basic friends system using the initial post in this thread as the basis of what is feasible. Note your own response doesn't indicate this workflow would be an issue.

Would it be possible for you or someone on your team to review our implementation? I'd be happy to walk you through it personally. We're limiting keys to 50 currently but I expect we could lower that, maybe to 25.

0 Likes 0 ·
Show more comments
Show more comments
rob avatar image
rob answered
@Brendan"we do plan to provide a two-way friend system at a later date"

Any ETA on this? This would be very useful for us so if it's going to be ready in the near future I'd rather wait than attempt to roll our own inferior solution.

Also, what would be the best way to overcome the downside you mentioned if we were to roll our own? Is it possible to call GetFriendLeaderboard and filter by Tags? Or do we need to call GetFriendsList every time we load a friend leaderboard and check for tags?

1 comment
10 |1200

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

brendan avatar image brendan ♦♦ commented ·

Sorry, but until something is in active development, we cannot provide a date. As a live service, we constantly have to make adjustments to our schedule, and we never want someone to take a dependency on us for a date we're not 100% confident we will meet.

For the leaderboard issue, you can do one of two things:

1. Just let them see the folks who haven't confirmed yet. The scores aren't private in any way (they can request them for the other player regardless).

2. Get the friends list for the player and filter the leaderboard returned, as you describe.

0 Likes 0 ·
raj avatar image
raj answered

I have a question as to how often I should call the cloud script (recommended). I have implemented Cloud Script to write to tags as suggested and the system works great.

The only thing is if player A invites player B, the only way player B knows this is to do a PlayFabClientAPI.GetFriendsList() call, so that he will get the new tags.

In my ;live' invite UI both players show 'Invite' Player A invites player B, I get a response and change the UI to say 'Invited', but player B still says 'Invite'. UNTIL I call GetFriendList again, then he can update his UI to say 'Requested'.

Currently I'm doing this every 10 seconds whilst the user is on that UI page. It's not a page that 100s of users are likely to be on at any one time, however I'd like to know what would be the recommended limit for calling PlayFabClientAPI.GetFriendsList() is 10 seconds too much, is it fine, can I do it alot more frequently? etc.

1 comment
10 |1200

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

Andy avatar image Andy ♦♦ commented ·

There's nothing terribly bad about every 10 seconds, but that has to be considered in the context of what else you're doing from your client with PlayFab. You want to keep overall request volume below about one every couple seconds at peek per client to avoid throttling. Personally, it seems like it would be okay to just query friends once when the page comes up, but I'm not familiar with your game or the scenario in detail.

0 Likes 0 ·

Write an Answer

Hint: Notify or tag a user in this post by typing @username.

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

Error rendering WebPanel (widgets/consolidation-widget.ftl): org.hibernate.hql.internal.ast.QuerySyntaxException: AvailableConsolidation is not mapped [from AvailableConsolidation up where up.node = :node]