Skip to main content

Group

1. Overview

PGOS Group is a service to create a persistent grouping mechanism for players to chat and interact with each other. A player can create or join in multiple groups. Roles of group members are provided to implement various group management features.

2. Group Management

2.1 Key Concepts of Group

Two structures are designed to describe a group in different scenarios.

  • FClientGroupInfo: It can be accessed by players both in or not in the group.
  • FClientGroupDetail: It can be only accessed by players in the group.

Key fields in FClientGroupInfo and FClientGroupDetail are as follows:

  • group_id: The unique identifier of a group.
  • name: Each group has a name to memorize or search. The maximum character limit is 300.
  • icon: Uri of the group icon, player can configure a cool icon for groups they create. The maximum character limit is 1024.
  • description: Descriptive text that introduces your group to other players, whether they are in the group or not. The maximum character limit is 1024.
  • tags: Tags can be used to define the classification of groups.
  • owner_info: The player info of group owner
  • member_count: Current member count of the group.
  • max_members: Max count of the group member. This field is fixed to 200 by PGOS.
  • announcement: Announcement text that can be only accessed by group members.
  • join_rule: The rules for players to join the group.
    • Any: Means anyone with this group id can join the group.
    • MemberInvite: Must be invited by group members to join the group.
    • AdminInvite: Must be invited by group administrator or owner to join the group.
    • Forbid: No one is allowed to join until the join rule changed.
  • is_public: True if the group can be seen by player who are not in the group.
  • is_offical: Identifies whether the group was created by the official staff of the game.

FClientGroupMember is designed to describe players in a group. Key fields in FClientGroupMember are as follows:

  • player_info: Basic info of the group member with type of FPlayerInfo.
  • nickname: Each group member could have a nick name.
  • remark: Member's remark can be set by group owner or administrator. Remark can be used to customize the classification of group members.
  • is_owner: True means the group is owned by this player.
  • is_admin: True means the player is an administrator.

2.2 Create / Join / Leave a Group

A player can create up to 5 groups by calling UPgosGroupAPI::CreateGroup API. The player who creates the group will be the group owner. A pointer to FClientGroupDetail structure will be returned if the API executes successfully.

#include "PgosSDKCpp.h"

void SomeUObjectClass::SomeFunction()
{
auto Group = IPgosSDKCpp::Get().GetClientGroupAPI();
if (Group)
{
FClientCreatGroupParams params;
// Fill create group params
Group->CreateGroup(params, [](const FPgosResult& Ret, const FClientGroupDetail* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("CreateGroup Successfully"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("CreateGroup Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

A player can join a existing group by calling UPgosGroupAPI::JoinGroup API. It should be noted that each player can join up to 100 groups, including the groups they created. A pointer to FClientGroupDetail structure will be returned if the API is called successfully.

#include "PgosSDKCpp.h"

void SomeUObjectClass::SomeFunction()
{
auto Group = IPgosSDKCpp::Get().GetClientGroupAPI();
if (Group)
{
FString GroupID;
Group->JoinGroup(GroupID, [](const FPgosResult& Ret, const FClientGroupDetail* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("JoinGroup Successfully"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("JoinGroup Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

A player can leave a group by calling UPgosGroupAPI::LeaveGroup API. A group owner can not leave the group unless the ownership is transferred to another group member.

#include "PgosSDKCpp.h"

void SomeUObjectClass::SomeFunction()
{
auto Group = IPgosSDKCpp::Get().GetClientGroupAPI();
if (Group)
{
FString GroupID;
Group->LeaveGroup(GroupID, [](const FPgosResult& Ret, const FClientGroupDetail* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("LeaveGroup Successfully"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("LeaveGroup Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

A group owner can dismiss a group permanently by calling UPgosGroupAPI::DismissGroup API. All group members except the owner will receive a OnGroupDismissed event when the group is dismissed.

#include "PgosSDKCpp.h"

void SomeUObjectClass::SomeFunction()
{
auto Group = IPgosSDKCpp::Get().GetClientGroupAPI();
if (Group)
{
FString GroupID;
Group->DismissGroup(GroupID, [](const FPgosResult& Ret, const FClientGroupDetail* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("DismissGroup Successfully"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("DismissGroup Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

2.3 Create an Official Group

Official group is designed to assist game operators to manage and guide players to create a better social environment in game. The differences between the official group to the non-official group are as follows

  • An official group can only be created via PGOS web portal.
  • An official group has a larger limit on the number of group members.
  • Official groups do not take up the group owner's group creation limit.

You can create an official group on the web portal as shown below.

image-20220629183113584

2.4 Groups Searching

UPgosGroupAPI::SearchGroup is provided to search public groups, no matter whether the groups are official or not. Filters can be used when searching for groups:

  • official_filter: filter the is_official field of the groups.
  • left_room_filter: use this filter to specify whether the retrieved group is full.
  • tags_filter: filter groups with specified tags.

If no filters are specified, a set of PGOS recommended groups will be returned.

#include "PgosSDKCpp.h"

void SomeUObjectClass::SomeFunction()
{
FClientSearchGroupParams params;
params.official_filter = FClientGroupOfficialFilterType::Dummy;
params.left_room_filter = FClientGroupRoomFilterType::Dummy;
params.tags_filter = [];
params.order_by = FClientGroupOrderBy::Dummy;
params.order_type = FClientGroupOrderType::Dummy;
params.offset = 0;
params.count = 20;
auto Group = IPgosSDKCpp::Get().GetClientGroupAPI();
if (Group)
{
Group->SearchGroup(params, [](const FPgosResult& Ret, const SearchGroupRsp* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("SearchGroup Successfully"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("SearchGroup Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}


2.5 Group Invitation

Another way to join a group is being invited by group members. The invitation behavior is constrained by the join_rule parameter.

A OnGroupInvitation event will be triggered if the invitee is online, PGOS will cache invitations for offline players. After a player logs in, the UPgosGroupAPI::GetGroupInvitations API can be called to retrieve all invitations sent to the player.

Players can accept an invitation by calling UPgosGroupAPI::AcceptGroupInvitation API, then the player will automatically join the group. Players can reject a invitation by calling UPgosGroupAPI::RejectGroupInvitation API. The invitation that has been processed will be removed from the player's invitation list.

#include "PgosSDKCpp.h"

// Invite some players with msg 'Join us for having fun!'
void SomeUObjectClass::SomeFunction()
{
auto Group = IPgosSDKCpp::Get().GetClientGroupAPI();
if (Group)
{
FClientInvitePlayersToGroupParams Params;
// Fill invite players params
Group->InvitePlayersToGroup(Params, [](const FPgosResult& Ret) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("InvitePlayersToGroup Successfully"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("InvitePlayersToGroup Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

// Get group invitation list
void SomeUObjectClass::SomeFunction()
{
auto Group = IPgosSDKCpp::Get().GetClientGroupAPI();
if (Group)
{
Group->GetGroupInvitations(GroupID, [](const FPgosResult& Ret, const TArray<FClientGroupInvitation>* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("GetGroupInvitations Successfully"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("GetGroupInvitations Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

// Accept a group invitation
void SomeUObjectClass::SomeFunction()
{
auto Group = IPgosSDKCpp::Get().GetClientGroupAPI();
if (Group)
{
FString GroupID;
FString InvitationId;
Group->AcceptGroupInvitation(GroupID, [](const FPgosResult& Ret, const FClientAcceptGroupInvitationRsp* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("AcceptGroupInvitation Successfully"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("AcceptGroupInvitation Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

// Reject a group invitation
void SomeUObjectClass::SomeFunction()
{
auto Group = IPgosSDKCpp::Get().GetClientGroupAPI();
if (Group)
{
FString GroupID;
FString InvitationId;
Group->RejectGroupInvitation(GroupID, [](const FPgosResult& Ret, const FClientRejectGroupInvitationRsp* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("RejectGroupInvitation Successfully"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("RejectGroupInvitation Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

2.6 Modify Group Info

Only group owner or administrator can modify group info. The fields that can be modified are as follows:

FieldAPI
nameUPgosGroupAPI::SetGroupName
iconUPgosGroupAPI::SetGroupIcon
descriptionUPgosGroupAPI::SetGroupDesc
announcementUPgosGroupAPI::SetGroupAnnouncement
join_ruleUPgosGroupAPI::SetGroupJoinRule
is_publicUPgosGroupAPI::SetGroupPublic

The interfaces above follow the same pattern. The sample code is given below by taking SetGroupName as an example.

#include "PgosSDKCpp.h"

// Invite some players with msg 'Join us for having fun!'
void SomeUObjectClass::SomeFunction()
{
FClientSetGroupNameParams Params;
// Fille set group name params;
auto Group = IPgosSDKCpp::Get().GetClientGroupAPI();
if (Group)
{
Group->SetGroupName(Params, [](const FPgosResult& Ret, const FClientGroupDetail* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("SetGroupName Successfully"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("SetGroupName Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

2.7 Manage Group Members

Group members with different roles have different group management permissions:

OperationRolesTarget PeoplePermission
Set remarkOwnerAny players in the group
AdministratorAny players in the group
MuteOwnerAny players in the group except himself
OwnerHimself
AdministratorAny players in the group except himself and owner
AdministratorOwner and himself
UnmuteOwnerAny players in the group
AdministratorAny players in the group
RemoveOwnerAny players in the group except himself
OwnerHimself
AdministratorGeneral group members
AdministratorOwner, himself and other adminisrators

Examples of these interfaces are given below.

Use UPgosGroupAPI::GetGroupMembers API to get the set of group members. The response data is a disordered map, so that the game should arrange the order of players in the member list on demand.

#include "PgosSDKCpp.h"

struct FClientGetGroupMembersRsp
{
/** Group members, key: player id, value: group member information. */
TMap<FString, FClientGroupMember> group_members;

/** Muted group members, key: player id, value: muted member information. */
TMap<FString, FClientMutedGroupMember> muted_members;
};

void SomeUObjectClass::SomeFunction()
{
auto Group = IPgosSDKCpp::Get().GetClientGroupAPI();
if (Group)
{
Group->GetGroupMembers(GroupID, [](const FPgosResult& Ret, const FClientGetGroupMembersRsp* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("GetGroupMembers Successfully"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("GetGroupMembers Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

Use UPgosGroupAPI::GetGroupMember API to get a group member info.

#include "PgosSDKCpp.h"

struct FClientGetGroupMemberRsp
{
/** Whether the player is a member of a group. */
bool is_member;

/** Group member information. */
FClientMutedGroupMember member_info;
};

void SomeUObjectClass::SomeFunction()
{
auto Group = IPgosSDKCpp::Get().GetClientGroupAPI();
if (Group)
{
Group->GetGroupMember(GroupID, PlayerID, [](const FPgosResult& Ret, const FClientGetGroupMemberRsp* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("GetGroupMember Successfully"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("GetGroupMember Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

Use UPgosGroupAPI::SetMyGroupNickname API to modify nick name of the currently logged in player.

#include "PgosSDKCpp.h"

void SomeUObjectClass::SomeFunction()
{
auto Group = IPgosSDKCpp::Get().GetClientGroupAPI();
if (Group)
{
SetMyGroupNickname Params;
// Fille set my group nickname params
Group->SetMyGroupNickname(Params, [](const FPgosResult& Ret) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("SetMyGroupNickName Successfully"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("SetMyGroupNickName Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

Use UPgosGroupAPI::RemoveGroupMember API to remove player from a group.

#include "PgosSDKCpp.h"

void SomeUObjectClass::SomeFunction()
{
auto Group = IPgosSDKCpp::Get().GetClientGroupAPI();
if (Group)
{
FString GroupId;
FString PlayerId;
Group->RemoveGroupMember(GroupID, PlayerId, [](const FPgosResult& Ret) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("RemoveGroupMember Successfully"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("RemoveGroupMember Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

Use UPgosGroupAPI::SetGroupMemberRemark API to set remark for group members. This API have a batch edition UPgosGroupAPI::BatchSetGroupMemberRemark.

#include "PgosSDKCpp.h"

void SomeUObjectClass::SomeFunction()
{
auto Group = IPgosSDKCpp::Get().GetClientGroupAPI();
if (Group)
{
FClientSetGroupMemberRemarkParams Params;
// Fill params
Group->SetGroupMemberRemark(Params, [](const FPgosResult& Ret) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("SetGroupMemberRemark Successfully"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("SetGroupMemberRemark Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

Use UPgosGroupAPI::MuteGroupMember and UPgosGroupAPI::UnmuteGroupMember to mute or mute a groum member.

#include "PgosSDKCpp.h"

// Mute
void SomeUObjectClass::SomeFunction()
{
auto Group = IPgosSDKCpp::Get().GetClientGroupAPI();
if (Group)
{
FString GroupId;
FString PlayerId;
int32 MuteMins = 1;
Group->MuteGroupMember(GroupID, PlayerId, MuteMins, [](const FPgosResult& Ret, const FClientGroupMember* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("MuteGroupMember Successfully"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("MuteGroupMember Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

// Unmute
void SomeUObjectClass::SomeFunction()
{
auto Group = IPgosSDKCpp::Get().GetClientGroupAPI();
if (Group)
{
FString GroupId;
FString PlayerId;
Group->UnmuteGroupMember(GroupID, PlayerId, [](const FPgosResult& Ret) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("UnmuteGroupMember Successfully"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("UnmuteGroupMember Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

Use UPgosGroupAPI::GrantGroupAdmin and UPgosGroupAPI::RevokeGroupAdmin to grant or revoke administrator role. These API could only be by group owner.

#include "PgosSDKCpp.h"

// Grant
void SomeUObjectClass::SomeFunction()
{
auto Group = IPgosSDKCpp::Get().GetClientGroupAPI();
if (Group)
{
FString GroupId;
FString PlayerId;
Group->GrantGroupAdmin(GroupID, PlayerId, [](const FPgosResult& Ret) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("GrantGroupAdmin Successfully"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("GrantGroupAdmin Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

// Revoke
void SomeUObjectClass::SomeFunction()
{
auto Group = IPgosSDKCpp::Get().GetClientGroupAPI();
if (Group)
{
FString GroupId;
FString PlayerId;
Group->RevokeGroupAdmin(GroupID, PlayerId, [](const FPgosResult& Ret) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("RevokeGroupAdmin Successfully"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("RevokeGroupAdmin Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

User UPgosGroupAPI::TransferGroupOwnership to transfer group owner role to a group member or administrators. These API could only be by group owner.

#include "PgosSDKCpp.h"

void SomeUObjectClass::SomeFunction()
{
auto Group = IPgosSDKCpp::Get().GetClientGroupAPI();
if (Group)
{
FString GroupId;
FString PlayerId;
Group->TransferGroupOwnership(GroupID, PlayerId, [](const FPgosResult& Ret) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("TransferGroupOwnership Successfully"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("TransferGroupOwnership Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

2.8 Get Group Info / Detail

Use UPgosGroupAPI::GetGroupInfo to request basic information of a group that the player hasn't joined yet.

#include "PgosSDKCpp.h"

void SomeUObjectClass::SomeFunction()
{
auto Group = IPgosSDKCpp::Get().GetClientGroupAPI();
if (Group)
{
FString GroupId;
Group->GetGroupInfo(GroupID, [](const FPgosResult& Ret, const FClientGroupInfo* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("GetGroupInfo Successfully"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("GetGroupInfo Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

Use UPgosGroupAPI::GetGroupDetail to request detail information of a group that the player has joined. This API have a batch edition UPgosGroupAPI::BatchGetGroupDetail.

#include "PgosSDKCpp.h"

void SomeUObjectClass::SomeFunction()
{
auto Group = IPgosSDKCpp::Get().GetClientGroupAPI();
if (Group)
{
FString GroupId;
Group->GetGroupDetail(GroupID, [](const FPgosResult& Ret, const FClientGroupDetail* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("GetGroupDetail Successfully"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("GetGroupDetail Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

2.9 Group Discovery

Use UPgosGroupAPI::GetGroupDiscoveryList to paging to get the number of group discoveries. You can additionally obtain the specified number of recommendation groups configured on the portal.

  • The Group service calculates a score for each group based on activity and other rules on a daily basis. The top 20,000 groups with the highest scores are selected and sorted in descending order. You can use the offset and count parameters of GetGroupDiscoveryList API to get groups by page.
  • Games can also obtain additional recommended groups. Game operators can configure a certain number of recommended groups on the portal, and some of these groups can be pinned to the top. Game developers can use the recommend_count parameter of the GetGroupDiscoveryList API to retrieve a specified maximum number of recommended groups. During retrieval, the API will randomly select recommend_count recommended groups from the configured ones on the portal, retrieve the top groups first.
  • The GetGroupDiscoveryList API also has an owner_os parameter. Games can use this parameter to retrieve groups where the group owner's operating system matches the specified OS. In most cases, this parameter can be left empty.

img

Interface prototype:

struct FClientGetGroupDiscoveryListParams 
{
/** Only obtain groups where the group owner's OS is the specified OS. Currently supports passing in at most one OS. Empty means no filtering based on OS. */
TArray<EClientOS> owner_os;
/**
* Get the number of recommended groups additionally, cannot exceed 50.
* Recommended groups can be configured on the portal.
*/
int32 recommend_count = 0;
/** The starting position of the list. */
int32 offset = 0;
/** The query count, cannot exceed 50. */
int32 count = 0;
};

/**
* Paging to get the number of group discoveries. You can additionally obtain the specified number of recommendation groups configured on the portal.
*
* @param Params
*/
void GetGroupDiscoveryList(
const FClientGetGroupDiscoveryListParams& Params,
TFunction<void(const FPgosResult& Ret, const FClientGetGroupDiscoveryListResult* Data)> Callback) const;

Example Code:

#include "PgosSDKCpp.h"

void SomeUObjectClass::SomeFunction()
{
auto Group = IPgosSDKCpp::Get().GetClientGroupAPI();
if (Group)
{
FClientGetGroupDiscoveryListParams Params;
Group->GetGroupDiscoveryList(Params, [](const FPgosResult& Ret, const FClientGetGroupDiscoveryListResult* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("GetGroupDiscoveryList Success"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("GetGroupDiscoveryList Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

2.10 Custom Data Handling

Group Custom Data is used to help game to extend the group. Each Group has two tiers of custom data: Global Custom Data and Player Custom Data. Game developers can store any data in there to implement the specific functionality.

Global Custom Data is a k-v map, that stores the data for the global scope. All members can visit it, but only group owner and administer has permission to store and modify in the game client.

Player Custom Data is a string->string (playerid -> data) map, that stores the data for every group members, the key is the playerid and the value is the data that is set by the owner member. All members can visit it, but the player only has permission to store and modify their own player custom data. Group owner and administer could modify all group members player custom data.

All members will receive the GroupGlobalCustomDataChangedEvt or GroupPlayerCustomDataChangedEvt event in the game client when any custom data was changed, including global custom data and player custom data.

sequenceDiagram PlayerA(Owner)->>GroupService: modify global custom data PlayerB->>GroupService: modify playerB's custom data GroupService-->>PlayerB: notify custom data changed GroupService-->>PlayerA(Owner): notify custom data changed GroupService-->>PlayerC: notify custom data changed

Call SetGroupGlobalCustomData to set global custom data for a group:

  #include "PgosSDKCpp.h"
#include "Core/PgosErrorCode.h"

void SomeUObjectClass::SetGroupGlobalCustomData()
{
auto group = IPgosSDKCpp::Get().GetClientGroupAPI();
if (group)
{
SetGroupGlobalCustomDataParams params;
group->SetGroupGlobalCustomData(params, [](
const FPgosResult& Ret, const SetGroupGlobalCustomDataResult * Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("Success"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

Call DelGroupGlobalCustomData to delete any fields of global custom data by keys:

  #include "PgosSDKCpp.h"
#include "Core/PgosErrorCode.h"

void SomeUObjectClass::DelGroupGlobalCustomData()
{
auto group = IPgosSDKCpp::Get().GetClientGroupAPI();
if (group)
{
DelGroupGlobalCustomDataParams params;
group->DelGroupGlobalCustomData(params, [](
const FPgosResult& Ret, const DelGroupGlobalCustomDataResult * Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("Success"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

Call ClearGroupGlobalCustomData to clear all group global custom data fields:

  #include "PgosSDKCpp.h"
#include "Core/PgosErrorCode.h"

void SomeUObjectClass::ClearGroupGlobalCustomData()
{
auto group = IPgosSDKCpp::Get().GetClientGroupAPI();
if (group)
{
ClearGroupGlobalCustomDataParams params;
group->ClearGroupGlobalCustomData(params, [](const FPgosResult& Ret) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("Success"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

Call SetGroupMyPlayerCustomData to set player owned custom data:

  #include "PgosSDKCpp.h"
#include "Core/PgosErrorCode.h"

void SomeUObjectClass::SetGroupMyPlayerCustomData()
{
auto group = IPgosSDKCpp::Get().GetClientGroupAPI();
if (group)
{
SetGroupMyPlayerCustomDataParams params;
group->SetGroupMyPlayerCustomData(params, [](
const FPgosResult& Ret, const SetGroupMyPlayerCustomDataResult * Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("Success"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

Call BatchSetGroupPlayerCustomData to batch set player owned custom data. Please note that only group owner or administer has permission to modify other group member's player custom data:

  #include "PgosSDKCpp.h"
#include "Core/PgosErrorCode.h"

void SomeUObjectClass::BatchSetGroupPlayerCustomData()
{
auto group = IPgosSDKCpp::Get().GetClientGroupAPI();
if (group)
{
BatchSetGroupPlayerCustomDataParams params;
group->BatchSetGroupPlayerCustomData(params, [](
const FPgosResult& Ret, const BatchSetGroupPlayerCustomDataResult * Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("Success"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

2.11 Group Management Events

To summarize, the events that will be sent to players by group service are listed below:

Event NameEvent Description
OnGroupInvitationThe event will be triggered when received a group invitation.
OnRemovedFromGroupThe event will be triggered when a player is removed from the group.
OnMutedInGroupThe event will be triggered when a player is muted in the group.
OnUnmutedInGroupThe event will be triggered when a player is unmuted in the group.
OnGroupDismissedThe event will be triggered when the group is dismissed by group owner.
OnBadgeNumOfGroupInvitationThe event will be triggered when a player logs in PGOS or the group invitation list changes.

You can use UPgosClientEventDispatcher to handle event binding issues for these events.

Bind dynamic delegate to OnGroupInvitation as follows,

  #include "PgosSDKCpp.h"

void SomeUObjectClass::SomeFunction()
{
auto Group = IPgosSDKCpp::Get().GetClientGroupAPI();
if (Group)
{
Group->OnGroupInvitation().AddUObject(this, &SomeUObjectClass::OnGroupInvitation);
}
}

void SomeUObjectClass::OnGroupInvitation(const FClientGroupInvitationEvt& event)
{
UE_LOG(LogTemp, Log, TEXT("OnGroupInvitation"));
}
  #include "PgosClientEventDispatcher.h"

void SomeUObjectClass::SomeFunction()
{
auto pDispatcher = UPgosClientEventDispatcher::GetPgosClientEventDispatcher();
if (pDispatcher)
{
pDispatcher->OnPlayerSessionChanged.Clear();
// add delegate callback
UPgosClientEventDispatcher::GetPgosClientEventDispatcher()->OnGroupInvitation.AddDynamic(
this,
&SomeUObjectClass::OnGroupInvitation);
// Before current object is destroyed, you should call
// `UPgosClientEventDispatcher::GetPgosClientEventDispatcher()->RemoveAll(this)`
// to clear event bindings.
}
}

void SomeUObjectClass::OnGroupInvitation(const FClientGroupInvitationEvt& event)
{
UE_LOG(LogTemp, Log, TEXT("OnGroupInvitation"));
}

3. Group Chat

3.1 General Sequence

Here is a general sequence for using the group chat:

sequenceDiagram participant t as Client(Tom) participant group as PGOS Group participant j as Client(Jerry) t->>group: Get Group List group-->>t: Return group list Tom joins j->>group: Join group(GroupA) group-->>t: Event(OnReceiveGroupMsg): [Event Msg] Jerry joins group-->>j: Event(OnReceiveGroupMsg): [Event Msg] Jerry joins t->>group: Send group message group-->>j: Event(OnReceiveGroupMsg): message from Tom t->>group: Leave group(GroupA) group-->>j: Event(OnReceiveGroupMsg): [Event Msg] Tom leaves

A possible group chat interaction is as follows:

  1. After successful login, the group entry icon shows the number of unread group messages.
  2. Click on the group icon to open the group chat window.
  3. The window first displays a list of the group chat conversations.
  4. Select a conversation to display the chat content with the corresponding group.
  5. Chat in group.

img

3.2 Data Structure

Group chat uses two import data structures: FClientGroupMsgInfo and FClientGroupChatItem.

  • FClientGroupMsgInfo: used to display message content,
  • FClientGroupChatItem: used to display player conversations.
struct FClientGroupMsgInfo
{
/** The member who sends the message */
FClientGroupMember sender;
/** The time the message was sent */
int64 sent_time;
/** The type of messages */
EClientChatMsgType msg_type;
/** The message to be sent */
FClientGroupMsgContent content;
/** The sequence number of messages */
int64 seq;
/** Additional information added by the game */
FString custom_data;
/**
* The mentioned player list.
* The mentioned players will receive the corresponding events. When the value of player_id is '@ALL', it means mentioning all players.
*/
TArray<FClientMentionedPlayer> mentioned_players;
};

struct FClientGroupChatItem
{
/** Group brief introduction */
FClientGroupBrief group_brief;
/** Recent chat message info */
FClientGroupMsgInfo latest_msg;
/** Unread messages count */
int32 unread_count;
};

enum class EClientChatMsgType : uint8
{
/** Type of text message, sent via SendGroupTextMsg. */
MsgTypeText = 0,
/** Type of custom message, sent via SendGroupCustomMsg. */
MsgTypeCustom = 1,
/** Type of event message, sent by the system. */
MsgTypeEvent = 2
};

struct FClientGroupMsgContent
{
/** Take the value from here when msg type is MsgTypeText */
FString text_content;
/** Take the value from here when msg type is MsgTypeEvent */
FClientGroupMsgEventContent event_content;
/** Take the value from here when msg type is MsgTypeCustom */
FString custom_content;
};

The EClientChatMsgType type corresponds to the fields in FClientGroupMsgContent.

Currently, EClientChatMsgType includes MsgTypeText , MsgTypeCustom and MsgTypeEvent.

  • When EClientChatMsgType is MsgTypeText, the message content is in text_content of FClientGroupMsgContent.
  • When EClientChatMsgType is MsgTypeCustom, the message content is in custom_content of FClientGroupMsgContent.
  • When EClientChatMsgType is MsgTypeEvent, the message content is in event_content of FClientGroupMsgContent.

In addition to the data structures above, you may also want to understand the following concepts:

  • seq: The sequence number of the message increases as players receive more messages from a given player.
  • unread group chat messages: group chat messages that are not read.

3.2.1 Group Chat Storage

The sent and received messages in a group chat are stored in the client's local database. Each account uses a separate database file to save group chat messages. You can configure the chat_msg_cache_max_bytes value to limit the max size of a single database file. We also provide the GetLocalCacheSize API to obtain the database file size and the ClearLocalCache API to clear chat records. You can use these APIs to prompt the user to clear chat records when the database file is too large.

Note:

The minimum value of chat_msg_cache_max_bytes is 1 MB (1048576). We do not recommend setting a value over 100 MB. If you do not specify a value, it takes the default value of 100 MB. When a single database file exceeds the value of chat_msg_cache_max_bytes, PGOS will automatically delete older messages to free up space.

3.2.2 Event Message Filtering

There are now a variety of event messages, and the game may require all or part of the types.

All event messages are supported by default, all event messages will trigger the OnReceiveGroupMsg event, and all event message types will also appear in the GetMyGroupChatList, GetGroupMsgList interfaces. If the game only needs some event message types and completely filter out other event messages, it can be modified through the configuration file (pgos_client.ini) or UPgosClientAPI::InitConfig API:

  • group_event_msg_filter is empty: all event message types are required.
    group_event_msg_filter =
  • group_event_msg_filter is 0 (the value of EClientGroupEventType::Dummy): Indicates that no event message is required, and all event messages are filtered out.
    group_event_msg_filter = 0
  • group_event_msg_filter for multiple event types: use "," to concatenate the value of the event type of the event message that needs to be supported.
    ; For example, only need GroupNameChanged, GroupDescChanged, GroupMemberJoined
    group_event_msg_filter = 1,2,3

Note:

Event messages are not counted as unread.

3.3 Using Group Chat

3.3.1 Obtain My Group List

When players are preparing to initiate a group chat, they may need to use the GetMyGroupList API to view the joined group list.

/**
* Get my group chat list
*/
void GetMyGroupChatList(TFunction<void(const FPgosResult& Ret, const FClientGetMyGroupChatListResult* Data)> Callback) const;

The returned result is saved in the FClientGetMyGroupChatListResult structure. The priority of the return value list is as follows: Number of unread messages > time of the last message.

struct FClientGetMyGroupChatListResult
{
/** My group conversation list */
TArray<FClientGroupChatItem> group_chat_list;
};

struct FClientGroupChatItem
{
/** Group brief information */
FClientGroupBrief group_brief;
/** Recent chat message info */
FClientGroupMsgInfo latest_msg;
/** Unread message count */
int32 unread_count;
};

Call GetMyGroupChatList from Group module, as follows:

#include "PgosSDKCpp.h"

void SomeUObjectClass::SomeFunction()
{
auto Group = IPgosSDKCpp::Get().GetClientGroupAPI();
if (Group)
{

Group->GetMyGroupChatList([](const FPgosResult& Ret, const FClientGetMyGroupChatListResult* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("GetMyGroupChatList "));
}
else
{
UE_LOG(LogTemp, Log, TEXT("GetMyGroupChatList Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

3.3.2 Obtain Group Chat Message List

PGOS stores the group chat messages locally. You can use the GetGroupMsgList, paginated query function to obtain chat records of the specified group.

/**
* Get chat message list in a group
*
* @param GroupId The group ID
* @param StartSeq The start sequence number of the message to query, 0 means starting from the latest message.
* @param Count The count of messages to get
*/
void GetGroupMsgList(
const FString& GroupId,
int64 StartSeq,
int32 Count,
TFunction<void(const FPgosResult& Ret, const FClientGetGroupChatMsgListResult* Data)> Callback) const;

The returned results are saved in the FClientGetGroupChatMsgListResult structure:

struct FClientGetGroupChatMsgListResult
{
/** Chat messages list */
TArray<FClientGroupMsgInfo> msg_list;
/** Whether there are more messages */
bool has_more;
/** The starting sequence for the next query */
int64 next_seq;
};

The mechanism to locally store chat records and obtain chat records is shown in the image below:

img

Call GetGroupMsgList from Group module, as follows:

#include "PgosSDKCpp.h"

void SomeUObjectClass::SomeFunction()
{
auto Group = IPgosSDKCpp::Get().GetClientGroupAPI();
if (Group)
{

FString GroupId;
int64 StartSeq;
int32 Count;
Group->GetGroupMsgList(GroupId, StartSeq, Count, [](const FPgosResult& Ret, const FClientGetGroupChatMsgListResult* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("GetGroupMsgList "));
}
else
{
UE_LOG(LogTemp, Log, TEXT("GetGroupMsgList Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

3.3.3 Send Group Chat Message

Players can send messages to groups they have joined. After the group chat message is sent, other online players in the group will receive it. The group chat cannot get offline messages, that is, after the player goes online, he cannot get the messages sent in the group while he is offline.

There are two ways to send messages:

  • SendGroupTextMsg: Send a simple text message. The msg_type of the message is MsgTypeText, and the text content of the message is stored in content.text_content.

  • SendGroupCustomMsg: It is used to send game custom messages. The msg_type of the message is MsgTypeCustom, and the content of the message is stored in content.custom_content.

    /**
    * Send group chat text messages
    *
    * @param TextMsgReq Request struct for sending text message
    */
    void SendGroupTextMsg(
    const FClientSendGroupMsgParams& Params,
    TFunction<void(const FPgosResult& Ret, const FClientSendGroupMsgResult* Data)> Callback) const;

    /**
    * Send group chat custom messages
    *
    * @param CustomMsgReq Request struct for sending custom messages
    */
    void SendGroupCustomMsg(
    const FClientSendGroupMsgParams& Params,
    TFunction<void(const FPgosResult& Ret, const FClientSendGroupMsgResult* Data)> Callback) const;

Here you need to pay attention to the input parameters. Take the parameter FClientSendGroupMsgParams of the SendGroupTextMsg interface as an example:

struct FClientSendGroupMsgParams
{
/** Group ID */
FString group_id;
/** Text content */
FString content;
/** Additional information added by the game */
FString custom_data;
/**
* The mentioned player list.
* The mentioned players will receive the corresponding events. When the value of player_id is '@ALL', it means mentioning all players.
*/
TArray<FClientMentionedPlayer> mentioned_players;
};

content is generally the input content of the user, which will be filtered by content moderation. custom_data is used for additional information added by game developers and will not be filtered by content moderation. The caveat here is not to store user input in custom_data. mentioned_players is optional, used to mention specified players. Player_id of FClientMentionedPlayer is "@ALL" to mention all players. The mentioned players will receive the OnGroupNewMentioned event.

The returned results are saved in the FClientGroupMsgInfo structure, which is described in details above.

Call SendGroupTextMsg/SendGroupCustomMsg from Group module, as follows:

  #include "PgosSDKCpp.h"

void SomeUObjectClass::SendGroupTextMsg()
{
auto Group = IPgosSDKCpp::Get().GetClientGroupAPI();
if (Group)
{
FClientSendGroupMsgParams Params;
Params.group_id = FString("1234");
Params.content = FString("hello");
Params.custom_data = FString("");
FClientMentionedPlayer MentionedAll;
MentionedAll.player_id = FString("@ALL"); // mention all members in the group
Params.mentioned_players.Add(MentionedAll);
Group->SendGroupTextMsg(Params, [](const FPgosResult& Ret, const FClientSendGroupMsgResult* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("SendGroupTextMsg "));
}
else
{
UE_LOG(LogTemp, Log, TEXT("SendGroupTextMsg Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

void SomeUObjectClass::SendGroupCustomMsg()
{
auto Group = IPgosSDKCpp::Get().GetClientGroupAPI();
if (Group)
{
FClientSendGroupMsgParams Params;
Params.group_id = FString("1234");
Params.content = FString("hello");
Params.custom_data = FString("");
FClientMentionedPlayer MentionedPlayer;
MentionedPlayer.player_id = FString("123456"); // mention player "123456"
MentionedPlayer.player_display_name = FString("Will");
Params.mentioned_players.Add(MentionedAll);
Group->SendGroupCustomMsg(Params, [](const FPgosResult& Ret, const FClientSendGroupMsgResult* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("SendGroupCustomMsg "));
}
else
{
UE_LOG(LogTemp, Log, TEXT("SendGroupCustomMsg Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

3.3.4 Activate a Group Chat

Players may activate a specified group chat via the ActiveGroupChat API.

Generally, a player will constantly receive messages while in group chat. Each time they receive a message, the unread message count will increase, triggering an OnUnreadGroupMsgCountNotify event and OnReceiveGroupMsg event, notifying changes of total unread messages and unread messages for this group. When the player is in the group chat screen, new messages should be automatically marked as read. To avoid repeated changes in the total unread count triggers OnUnreadGroupMsgCountNotify event again, we provide the ActiveGroupChat API, which activates a specified group chat.

  • Before activation, each new message changes the unread message count and triggers an OnUnreadGroupMsgCountNotify event.
  • After activation, the unread message count for the specified group will remain at 0, so new messages will not trigger OnUnreadGroupMsgCountNotify events.

As the image shown below, you are currently chatting with GroupB. When you receive messages from groupB, they are marked as read by default and do not change the total unread messages count. When you are in the chat screen with Jerry, the ActiveGroupChat API activates the chat and Jerry's unread message count remains 0, so when you receive group chat messages from Jerry, it does not trigger OnUnreadGroupMsgCountNotify event. When you leave the chat screen, call the API with null GroupId so that the current conversation will be deactivated.

img

/**
* Set the current group chat. When setting, OnUnreadGroupMsgCountNotifyEvt for this group will NOT be pushed, and subsequent group chat messages currently in progress are automatically marked as read
*
* @param GroupId The group to set as active
*/
void ActiveGroupChat(const FString& GroupId) const;

Call ActiveGroupChat from Group module, as follows:

#include "PgosSDKCpp.h"

void SomeClass::ActiveGroupChat(const FString& GroupId)
{
auto GroupChat = IPgosSDKCpp::Get().GetClientGroupAPI();
if (GroupChat)
{
GroupChat->ActiveGroupChat(GroupId);
}
}

Note:

  • Only one group chat can be activated at a time.
  • Call with empty GroupId to deactivate the current conversation.

3.3.5 Clear Unread Group Chat Messages Count

If all the group chat messages from a specified group are read, you can use the ClearGroupUnreadCount API to set the unread messages count of this group to zero.

/**
* Mark messages in group as all read
*
* @param GroupId The group ID. If empty, it means all groups
*/
void ClearGroupUnreadCount(
const FString& GroupId,
TFunction<void(const FPgosResult& Ret)> Callback) const;

Call ClearGroupUnreadCount from Group module, as follows:

#include "PgosSDKCpp.h"

void SomeClass::ClearGroupUnreadCount(const FString& GroupId)
{
auto group = IPgosSDKCpp::Get().GetClientGroupAPI();
if (group)
{
group->ClearGroupUnreadCount(GroupId, [](const FPgosResult& Ret)
{
if (Ret.err_code == 0)
{
UE_LOG(LogTemp, Log, TEXT("ClearGroupUnreadCount "));
}
else
{
UE_LOG(LogTemp, Log, TEXT("ClearGroupUnreadCount Failed: err_code=(%d), err_msg=%s]"), Ret.err_code,
*Ret.msg);
}
});
}
}

3.3.6 Mention Players

Specified players can be mentioned when sending a message. For details, please view Send Group Chat Message

If someone in the group mentioned you, you will receive the OnGroupNewMentioned event.

You can query the last mentioned record of the specified group by calling the interface GetGroupMentionedInfo.

You can clear the records of the specified group mentioned you by calling the interface ClearGroupMentionedInfo. When group_id is empty, it means clearing the mentioned records of all groups.

Interface prototype:

/**
* Get latest record mentioning me in the group.
*
* @param GroupId Group ID.
* @param Dst The latest record mentioned me.
* @return True if there are any mentions of me.
*/
bool GetGroupMentionedInfo(
const FString& GroupId,
FClientGroupMentionedInfo& Dst) const;

/**
* Clear record mentioning me in the group.
*
* @param GroupId Group ID. If empty, it means all groups
*/
void ClearGroupMentionedInfo(const FString& GroupId) const;

Example Code:

#include "PgosSDKCpp.h"

void SomeUObjectClass::SomeFunction1()
{
auto Group = IPgosSDKCpp::Get().GetClientGroupAPI();
if (Group)
{

FString GroupId = TEXT("123");
FClientGroupMentionedInfo Dst;
auto result = Group->GetGroupMentionedInfo(GroupId, Dst);
if (result)
{
UE_LOG(LogTemp, Log, TEXT("%s mentioned you"), *Dst.msg.sender.player_info.display_name);
}
else
{
UE_LOG(LogTemp, Log, TEXT("No one mentioned you"));
}
}
}


void SomeUObjectClass::SomeFunction2()
{
auto Group = IPgosSDKCpp::Get().GetClientGroupAPI();
if (Group)
{
FString GroupId = TEXT("123");
Group->ClearGroupMentionedInfo(GroupId);
}
}

3.3.7 Monitor Group Chat Events

Some events will be triggered when using the group chat:

  • OnUnreadGroupMsgCountNotify: Immediately triggered after login and when the total unread count changes. The event results are saved in FClientUnreadMsgCountNotifyEvt.
  • OnReceiveGroupMsg: Triggered when receiving a group chat message. The event results are saved in FClientReceiveGroupMsgEvt.
  • OnGroupNewMentioned Triggered when receiving a group chat message that mentioned you. The event results are saved in FClientGroupNewMentionedEvt.
struct FClientUnreadMsgCountNotifyEvt
{
/** Total unread count of personal chat or group chat */
int32 total_unread_count;
};

struct FClientReceiveGroupMsgEvt
{
/** Group chat message received */
FClientGroupMsgInfo msg;
/** Group brief info */
FClientGroupBrief group_brief;
/** Count of unread message from group */
int32 unread_count;
};

struct FClientGroupNewMentionedEvt
{
/** Group chat message received */
FClientGroupMsgInfo msg;
/** Group brief info */
FClientGroupBrief group_brief;
};

OnReceiveGroupMsg: Possible received message types can be distinguished according to msg_type:

enum class EClientChatMsgType : uint8
{
/** Type of text message, sent via SendGroupTextMsg. */
MsgTypeText = 0,
/** Type of custom message, sent via SendGroupCustomMsg. */
MsgTypeCustom = 1,
/** Type of event message, sent by the system. */
MsgTypeEvent = 2
// New msg_types such as MsgTypeImage, MsgTypeVoice may be added in the future.
};

There are now three message types: MsgTypeText, MsgTypeCustom, MsgTypeEvent. Where MsgTypeText, MsgTypeCustom are sent by the player, MsgTypeEvent is sent by the system. Messages of type MsgTypeEvent are called event messages, and different event messages can be distinguished according to event_type:

enum class EClientGroupEventType : uint8
{
Unknown = 0,
/** Group name has been changed. */
GroupNameChanged = 1,
/** Group description has been changed. */
GroupDescChanged = 2,
/** New member joined. */
GroupMemberJoined = 3,
/** Member left the group. */
GroupMemberLeft = 4,
/** Member removed. */
GroupMemberRemoved = 5,
/** Group announcement has been changed. */
GroupAnnouncementChanged = 6,
/** Group owner has changed. */
GroupOwnerChanged = 7
// New event_types may be added in the future.
};

Monitor group chat events from Group module, as follows:

#include "PgosSDKCpp.h"

void SomeClass::MonitorGroupChatEvents()
{
auto GroupChat = IPgosSDKCpp::Get().GetClientGroupAPI();
if (GroupChat)
{
GroupChat->OnUnreadGroupMsgCountNotify().AddUObject(this, &SomeClass::OnUnreadGroupMsgCountNotify);
GroupChat->OnReceiveGroupMsg().AddUObject(this, &SomeClass::OnReceiveGroupMsg);
}
}

void SomeClass::OnUnreadGroupMsgCountNotify(const FClientUnreadMsgCountNotifyEvt& Event)
{
UE_LOG(LogTemp, Log, TEXT("OnUnreadGroupMsgCountNotify"));
}

void SomeClass::OnReceiveGroupMsg(const FClientReceiveGroupMsgEvt& Event)
{
UE_LOG(LogTemp, Log, TEXT("OnReceiveGroupMsg"));
const auto MsgType = Event.msg.msg_type;
if (MsgType == EClientChatMsgType::MsgTypeText)
{
UE_LOG(LogTemp, Log, TEXT("receive text msg, content=(%s)"), *Event.msg.content.text_content);
} else if (MsgType == EClientChatMsgType::MsgTypeCustom) {
UE_LOG(LogTemp, Log, TEXT("receive custom msg, content=(%s)"), *Event.msg.content.custom_content);
} else if (MsgType == EClientChatMsgType::MsgTypeEvent) {
const auto EventType = Event.msg.content.event_content.event_type;
UE_LOG(LogTemp, Log, TEXT("receive event msg, event_type=(%d)"), *EventType);
if (EventType == EClientGroupEventType::GroupNameChanged) {
const auto GroupNameChanged = Event.msg.content.event_content.group_name_changed;
UE_LOG(LogTemp, Log, TEXT("group name changed, new group_name=(%s)"), *GroupNameChanged.group_name);
} else if (EventType == EClientGroupEventType::GroupMemberJoined) {
const auto GroupMemberJoined = Event.msg.content.event_content.group_member_joined;
UE_LOG(LogTemp, Log, TEXT("new member joined, name=(%s)"), *GroupMemberJoined.member_info.display_name);
}
// ......
}
}

4. Portal Operation

You can query group list on the web portal as shown below.

image-20220622161030871

You can click Add Official Group button to create an official group.

image-20220629183034145

image-20220629162413198

You can enter a specified group and click Dismiss button to dismiss a group on the group detail page.

image-20220629162730982

You can query group list of a specified player on the web portal as shown below.

image-20220622161219867

5. Key Error Handling

Error CodeRelevant APIHandling Suggestion
PgosErrCode::kBackendProfaneNameCreateGroup ModifyGroupInfoWhen there are sensitive words, the API will return an error. Please check the input data of the API.
PgosErrCode::kBackendGroupNotPublicGetGroupInfoIt means that the group information does not allow unjoined players to access.
PgosErrCode::kBackendIMChatFrequencyLimitSendGroupTextMsg SendGroupCustomMsgIt means that the operation frequency exceeds limit. It's recommended to retry with exponentially expanded interval: 5s, 10s, 20s, etc.
PgosErrCode::kBackendIMChatMsgIsTooLongSendGroupTextMsg SendGroupCustomMsgIt means that the message content is too long. The limit is 1000 characters. The game should prompt player with a message like "Message too long, please shorten it or split it in multiple messages."
PgosErrCode::kBackendIMChatCustomDataIsTooLongSendGroupTextMsg SendGroupCustomMsgIt means that the custom data is too long. The limit is 8000 bytes. The game catch this error and avoid using super long custom data.
PgosErrCode::kBackendIMPlayerBlockedByAdminSendGroupTextMsg SendGroupCustomMsgIt means that the player is muted by admin. The game should prompt player with a message like "Muted by administrator, please retry later."
PgosErrCode::kBackendProfaneWordsSendGroupTextMsg SendGroupCustomMsg CreateGroup ModifyGroupInfoIt means the message content contains profane words and cannot be masked. The game should prompt the player with a message like "Please remove profane words to send message."
PgosErrCode::kBackendErrPlayerIsPunishedSendGroupTextMsg SendGroupCustomMsg ModifyGroupInfoThe player is punished. msg is the json serialized string of deadline(unix timestamp (in seconds) at the end of the punishment, <= 0 means permanent punishment) and reason.