GS2-Friend
Provides the ability to create a game-specific social graph. While social graphs are a feature that platforms also offer, there is also value in creating your own in-game social graph.
Players tend to be reluctant to register platform-wide friend relationships. This is because there is a case of “I want to play the game I’m playing with this person, but I don’t want to play other games with them”. If we treat the social graph provided by the platform as reflecting a relationship closer to real life, and the game-specific social graph as reflecting a relationship within that game, players will feel more comfortable using the friend feature.
Friends
In order to become friends, a mutual, consensual relationship is required.
A friend relationship is established when one of the players who wishes to become a friend sends a friend request to the other player, and the player who receives the friend request accepts it.
Follow
A follow can be established without the consent of the other party.
The person being followed has no way of knowing who is following them as a list. This specification may seem strange when you imagine the specifications of a social network follow. However, we have looked at the specifications of many games that implement the follow feature in-game and found that it is not necessary.
If a player who follows you goes on an adventure with your ghost character, it is sufficient to share the rewards obtained from the adventure, and there is little interest in knowing who accompanied you.
You cannot get a list, but when delivering a reward message to GS2-Inbox to share rewards earned from an adventure, it is possible to convey profiles by including the user IDs of the players who adventured together in the message payload.
Profile
GS2-Friend provides an area to hold a player’s profile. The profile can have any value and there are three areas.
- “Public Profile”, which can be freely viewed by other players.
- “Follower Profile”, which can be viewed by players who follow the player.
- “Friend Profile”, which can be viewed by friends.
Each of these can be used for different purposes.
Blacklist
After playing a game for a long time, there may be players who make you feel uncomfortable. This feature allows you to create a list of such players and make it permanent.
It is only a persistent feature, and simply adding them here does not perform any function. It is necessary to pass the list to GS2-Matchmaking’s matchmaking conditions, or use the list recorded here separately as needed.
Script Triggers
Setting followScript unfollowScript sendRequestScript cancelRequestScript acceptRequestScript rejectRequestScript deleteFriendScript updateProfileScript in the namespace allows custom scripts to be executed before and after each operation such as follows, friend requests, and profile updates.
Scripts support both synchronous and asynchronous execution, with asynchronous processing enabling external integration through GS2-Script or Amazon EventBridge. These settings can be templated and managed with GS2-Deploy or language-specific CDKs.
Main event triggers and script setting names are:
followScript(completion notification:followDone): before and after followsunfollowScript(completion notification:unfollowDone): before and after unfollowssendRequestScript(completion notification:sendRequestDone): before and after sending friend requestscancelRequestScript(completion notification:cancelRequestDone): before and after canceling friend requestsacceptRequestScript(completion notification:acceptRequestDone): before and after accepting friend requestsrejectRequestScript(completion notification:rejectRequestDone): before and after rejecting friend requestsdeleteFriendScript(completion notification:deleteFriendDone): before and after deleting friendsupdateProfileScript(completion notification:updateProfileDone): before and after profile updates
Push Notifications
The main push notifications and their configuration names are as follows:
followNotification: Notifies when followedreceiveRequestNotification: Notifies when a friend request is receivedcancelRequestNotification: Notifies when a friend request is canceledacceptRequestNotification: Notifies when a friend request is acceptedrejectRequestNotification: Notifies when a friend request is rejecteddeleteFriendNotification: Notifies when a friend is deleted
All notifications can be sent via GS2-Gateway and can be forwarded to offline devices as mobile push notifications.
Transaction Actions
GS2-Friend provides the following transaction actions:
- Acquire Action: Updating Profile
By using “Updating Profile” as an acquire action, it is possible to integrate profile updates into a series of transactions, such as changing a player name by consuming in-game currency.
Example implementation
Updating Profile
var result = await gs2.Friend.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).Profile(
).UpdateProfileAsync(
publicProfile: "public",
followerProfile: "follower",
friendProfile: "friend"
);
var item = await result.ModelAsync(); const auto Domain = Gs2->Friend->Namespace(
"namespace-0001" // namespaceName
)->Me(
AccessToken
)->Profile(
);
const auto Future = Domain->UpdateProfile(
"public", // publicProfile
"follower", // followerProfile
"friend" // friendProfile
);
Future->StartSynchronousTask();
if (Future->GetTask().IsError()) return false;
// obtain changed values / result values
const auto Future2 = Future->GetTask().Result()->Model();
Future2->StartSynchronousTask();
if (Future2->GetTask().IsError()) return false;
const auto Result = Future2->GetTask().Result();Get own profile
var item = await gs2.Friend.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).Profile(
).ModelAsync(); const auto Domain = Gs2->Friend->Namespace(
"namespace-0001" // namespaceName
)->Me(
AccessToken
)->Profile(
);
const auto item = Domain.Model();Get someone else’s public profile
var item = await gs2.Friend.Namespace(
namespaceName: "namespace-0001"
).User(
userId: "user-0001"
).PublicProfile(
).ModelAsync(); const auto Domain = Gs2->Friend->Namespace(
"namespace-0001" // namespaceName
)->User(
"user-0001" // userId
)->PublicProfile(
);
const auto item = Domain.Model();Send a friend request
var result = await gs2.Friend.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).SendRequestAsync(
targetUserId: "user-0002"
); const auto Future = Gs2->Friend->Namespace(
"namespace-0001" // namespaceName
)->Me(
AccessToken
)->SendRequest(
"user-0002"
);
Future->StartSynchronousTask();
if (Future->GetTask().IsError()) return false;Get a list of friend requests you have sent
var items = await gs2.Friend.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).SendRequestsAsync(
).ToListAsync(); const auto It = Gs2->Friend->Namespace(
"namespace-0001" // namespaceName
)->Me(
AccessToken
)->SendRequests(
);
TArray<Gs2::UE5::Friend::Model::FEzFriendRequestPtr> Result;
for (auto Item : *It)
{
if (Item.IsError())
{
return false;
}
Result.Add(Item.Current());
}Get a list of received friend requests
var items = await gs2.Friend.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).ReceiveRequestsAsync(
).ToListAsync(); const auto It = Gs2->Friend->Namespace(
"namespace-0001" // namespaceName
)->Me(
AccessToken
)->SendRequests(
);
TArray<Gs2::UE5::Friend::Model::FEzFriendRequestPtr> Result;
for (auto Item : *It)
{
if (Item.IsError())
{
return false;
}
Result.Add(Item.Current());
}Accept a friend request
var result = await gs2.Friend.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).ReceiveFriendRequest(
fromUserId: "user-0002"
).AcceptAsync(
); const auto Future = Gs2->Friend->Namespace(
"namespace-0001" // namespaceName
)->Me(
AccessToken
)->ReceiveFriendRequest(
nullptr, // targetUserId
"user-0002" // fromUserId
)->Accept(
);
Future->StartSynchronousTask();
if (Future->GetTask().IsError()) return false;Reject a friend request
var result = await gs2.Friend.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).ReceiveFriendRequest(
fromUserId: "user-0002"
).RejectAsync(
); const auto Future = Gs2->Friend->Namespace(
"namespace-0001" // namespaceName
)->Me(
AccessToken
)->ReceiveFriendRequest(
nullptr, // targetUserId
"user-0002" // fromUserId
)->Reject(
);
Future->StartSynchronousTask();
if (Future->GetTask().IsError()) return false;Get a list of friends
var items = await gs2.Friend.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).FriendsAsync(
).ToListAsync(); const auto It = Gs2->Friend->Namespace(
"namespace-0001" // namespaceName
)->Me(
AccessToken
)->Friends( // withProfile
);
TArray<Gs2::UE5::Friend::Model::FEzFriendUserPtr> Result;
for (auto Item : *It)
{
if (Item.IsError())
{
return false;
}
Result.Add(Item.Current());
}Delete a friend
var result = await gs2.Friend.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).Friend(
withProfile: null
).FriendUser(
targetUserId: "user-0002"
).DeleteFriendAsync(
); const auto Future = Gs2->Friend->Namespace(
"namespace-0001" // namespaceName
)->Me(
AccessToken
)->Friend(
nullptr // withProfile
)->FriendUser(
"user-0002" // targetUserId
)->DeleteFriend(
);
Future->StartSynchronousTask();
if (Future->GetTask().IsError()) return false;Follow a player
var result = await gs2.Friend.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).FollowUser(
targetUserId: "user-0002",
withProfile: true
).FollowAsync(
); const auto Future = Gs2->Friend->Namespace(
"namespace-0001" // namespaceName
)->Me(
AccessToken
)->FollowUser(
"user-0002", // targetUserId
true // withProfile
)->Follow(
);
Future->StartSynchronousTask();
if (Future->GetTask().IsError()) return false;Get a list of followers
var items = await gs2.Friend.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).FollowsAsync(
).ToListAsync(); const auto It = Gs2->Friend->Namespace(
"namespace-0001" // namespaceName
)->Me(
AccessToken
)->Follows(
);
TArray<Gs2::UE5::Friend::Model::FEzFollowUserPtr> Result;
for (auto Item : *It)
{
if (Item.IsError())
{
return false;
}
Result.Add(Item.Current());
}Unfollow a player
var result = await gs2.Friend.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).FollowUser(
targetUserId: "user-0002",
withProfile: true
).UnfollowAsync(
); const auto Domain = Gs2->Friend->Namespace(
"namespace-0001" // namespaceName
)->Me(
AccessToken
)->FollowUser(
"user-0002", // targetUserId
true // withProfile
);
const auto Future = Domain->Unfollow(
);
Future->StartSynchronousTask();
if (Future->GetTask().IsError())
{
return false;
}