Skip to main content

Party

1. Overview

Party is a service for creating an instant team for players. The player can use it to build a "temporary virtual team" and then fire a battle. It includes party creation, player invitations, custom data, and party event dispatching. There is a leader and multiple members in a party. All players have permission to create a party. Once created, the creator is the leader by default, but the leadership can be transferred to other members if necessary. Since the party is an instant team, the player will be removed from the party once leaving the party manually or offline.

2. Architecture Diagram

Game accesses the party service through the PGOS client SDK, and includes two types of interaction. One is client invocation via the HTTPS protocol. The other is the client event, which is a message pushed by the push service in the PGOS backend and implemented through a WebSocket persistent connection that is automatically established and maintained by the PGOS client SDK after authentication. All events are triggered by the SDK as push messages, which also notifies the game.

party_arch

3. Core Features and Rules

3.1 Create Party

Any player can create a party, but a player can only create/join one party concurrently.

Call CreateParty from Party module as follows:

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

void SomeUObjectClass::CreateParty()
{
auto party = IPgosSDKCpp::Get().GetClientPartyAPI();
if (party)
{
FClientCreatePartyParams Params;
Params.party_name = "A Name";
Params.max_player = 0;
Params.enable_chatting = false;
Params.join_strategy = EClientPartyJoinStrategy::Public;
Params.invite_strategy = EClientPartyInviteStrategy::AllMembersCanInvite;
party->CreateParty(Params, [](const FPgosResult& Ret, const FClientPartyInfo* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("OnCreatePartySuccess: party_id=%s"), *Data->party_id);
}
else
{
UE_LOG(LogTemp, Log, TEXT("OnCreatePartyFailed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

3.2 Party Invite and Join Control

In the code mentioned above of CreateParty, there are two parameters, join_strategy and invite_strategy, which are used to control how the party handles "joining" and "inviting."

join_strategy controls how other players join the party, there are four different strategies:

EClientPartyJoinStrategyDescription
PublicAny player can join the party, except for the player who is blocked by the leader.
It's the default setting.
OnlyFriendsOfLeaderOnly friends of the leader are allowed to join the party.
OnlyFriendsOfMembersOnly friends of any member who are not blocked by the leader are allowed to join the party.
OnlyInvitedOnly explicitly invited players who are not blocked by the leader are allowed to join the party.

invite_strategy controls how the members of the party invite others.

EClientPartyInviteStrategyDescription
AllMembersCanInviteAll members of the party are allowed to invite other players to join the party.
A player who is blocked by the leader or the inviting player can't be invited.
It's the default setting.
OnlyLeaderCanInviteOnly the leader is allowed to invite other players to join the party.
A player who is blocked by the leader can't be invited.

Only Leader can change join_strategy and invite_strategy after the party created.

Call SetPartyStrategy from Party module as follows:

#include "PgosSDKCpp.h"

void SomeUObjectClass::SetPartyStrategy()
{
auto Party = IPgosSDKCpp::Get().GetClientPartyAPI();
if (Party)
{
FClientSetPartyStrategyParams Params;
Params.party_id = PartyId;
Params.join_strategy = EClientPartyJoinStrategy::Public;
Params.invite_strategy = EClientPartyInviteStrategy::AllMembersCanInvite;
Party->SetPartyStrategy(Params, [](const FPgosResult& Ret) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("SetPartyStrategy Success"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("SetPartyStrategy Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

All members (except the leader) will receive a notification on the PartyStrategy changed. Bind dynamic delegate to OnPartyStrategyChanged as follows:

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

// Observe the notification of party strategies changes somewhere
void SomeUObjectClass::SomeFunction()
{
auto party = IPgosSDKCpp::Get().GetClientPartyAPI();
if (party)
{
party->OnPartyStrategyChanged().AddUObject(this, &SomeUObjectClass::OnPartyStrategyChanged);
}
}

// Handle the notification
void SomeUObjectClass::OnPartyStrategyChanged(const FClientPartyStrategyChangedEvt& event)
{
UE_LOG(LogTemp, Log, TEXT("OnPartyStrategyChanged"));
// Party members cannot change `join_strategy` and `invite_strategy` actively.
}

3.3 Invite and Join

Leader and members can invite other players to join a party, and one player can only join one party concurrently.

sequenceDiagram PlayerA->>PartyService: invite playerB PartyService-->>+PlayerB: notify inviting PlayerB->>-PartyService: PlayerB join PartyService-->>PlayerA: notify PlayerB joining PartyService-->>PlayerC: notify PlayerB joining

3.3.1 Inviting

Call InvitePlayerToParty from Party module as follows:

#include "PgosSDKCpp.h"

void SomeUObjectClass::InvitePlayerToParty()
{
auto Party = IPgosSDKCpp::Get().GetClientPartyAPI();
if (Party)
{

FString PartyId;
TArray<FString> InviteePlayerIds;
Party->InvitePlayerToParty(PartyId, InviteePlayerIds, [](const FPgosResult& Ret) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("InvitePlayerToParty Success"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("InvitePlayerToParty Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

Bind dynamic delegate to OnReceivePartyInvitation as follows:

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

// Observe the notification of party invitation somewhere
void SomeUObjectClass::SomeFunction()
{
auto party = IPgosSDKCpp::Get().GetClientPartyAPI();
if (party)
{
party->OnReceivePartyInvitation().AddUObject(this, &SomeUObjectClass::OnReceivePartyInvitation);
}
}

// Handle the invitation
void SomeUObjectClass::OnReceivePartyInvitation(const FClientReceivePartyInvitationEvt& event)
{
UE_LOG(LogTemp, Log, TEXT("OnReceivePartyInvitation"));
// After receiving the invitation, it will generally be handled according to one of the following situations:
// 1. After receiving the invitation, the game will pop up a dialog box asking the player whether to join the party. After the player agrees, call the JoinParty API to join the party.
// 2. After receiving the invitation, the game can directly call the JoinParty API to join the party.
}

3.3.2 Joining

Call JoinParty from Party module as follows:

#include "PgosSDKCpp.h"

void SomeUObjectClass::JoinParty()
{
auto Party = IPgosSDKCpp::Get().GetClientPartyAPI();
if (Party)
{
FString PartyId;
Party->JoinParty(PartyId, [](const FPgosResult& Ret, const FClientPartyInfo* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("JoinParty Success"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("JoinParty Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

3.4 Leader’s Privileges

  • Kick out members: a leader can kick out any member at any time.
  • Dismiss the party.
  • Transfer leadership to another member.

Call KickPartyMember/DismissParty/TransferPartyLeader from Party module as follows:

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

// Kick out members
void SomeUObjectClass::KickPartyMember()
{
auto party = IPgosSDKCpp::Get().GetClientPartyAPI();
if (party)
{
FString PartyId = ObtainFromSomeWhere();
FString KickPlayerID = ObtainFromSomeWhere();

party->KickPartyMember(PartyId, KickPlayerID, [](const FPgosResult& Ret) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("OnKickMemberSuccess"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("OnKickMemberFailed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

// Dismiss the party
void SomeUObjectClass::DismissParty()
{
auto party = IPgosSDKCpp::Get().GetClientPartyAPI();
if (party)
{
FString PartyId = ObtainFromSomeWhere();

party->DismissParty(PartyId, [](const FPgosResult& Ret) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("OnDismissPartySuccess"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("OnDismissPartyFailed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

// Transfer leader privilege to another member
void SomeUObjectClass::TransferPartyLeader()
{
auto party = IPgosSDKCpp::Get().GetClientPartyAPI();
if (party)
{
FString PartyId = ObtainFromSomeWhere();
FString NewLeaderPlayerId = ObtainFromSomeWhere();

party->TransferPartyLeader(PartyId, NewLeaderPlayerId, [](const FPgosResult& Ret) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("OnTransferLeaderSuccess"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("OnTransferLeaderFailed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

3.5 Dismiss Party

A party will be dismissed in the following cases:

  • Leader dismisses the party.

  • Party will be automatically dismissed after all members have left the party or have logged out.

    sequenceDiagram PlayerA->>PartyService: dismiss PartyService-->>PlayerB: notify dismissed PartyService-->>PlayerC: notify dismissed

    Check the previous section for sample codes.

3.6 Leave Party

  • Any player can leave the party. When the leader leaves the party, PGOS will automatically promote another member as the new leader.

  • A player will automatically leave the party after logging out.

    sequenceDiagram PlayerA->>PartyService: leave party / transfer leader PartyService-->>PlayerB: notify PlayerA left / leader transfered PartyService-->>PlayerC: notify PlayerA left / leader transfered

Call LeaveParty from Party module as follows:

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

// Leave the party
void SomeUObjectClass::LeaveParty()
{
auto party = IPgosSDKCpp::Get().GetClientPartyAPI();
if (party)
{
FString PartyId = ObtainFromSomeWhere();

party->LeaveParty(PartyId, [](const FPgosResult& Ret) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("OnLeavePartySuccess"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("OnLeavePartyFailed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

3.7 Custom Data Handling

Party Custom Data is used to help game to extend the party, such as "Party Ready Up", "Party Vote", etc. Every Party 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 string, that stores the data for the global scope. All members can visit it, but only party leader 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 party 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.

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

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

Call GetPartyInfo or CurrentParty to obtain the PartyInfo that contains PartyCustomData.

Call SetGlobalCustomData to set global custom data for a party. Please note that only party leader has permission to do it:

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

void SomeUObjectClass::SetGlobalCustomData()
{
auto party = IPgosSDKCpp::Get().GetClientPartyAPI();
if (party)
{
FString PartyId = ObtainFromSomeWhere();

party->SetGlobalCustomData(PartyId, GlobalCustomData, [](
const FPgosResult& Ret, const PartyCustomData * Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("OnSetGlobalCustomDataSuccess"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("OnSetGlobalCustomDataFailed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

Call SetPlayerCustomData toplayer owned custom data. Please note that the player only has permission to store and modify their own player custom data:

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

void SomeUObjectClass::SetPlayerCustomData()
{
auto party = IPgosSDKCpp::Get().GetClientPartyAPI();
if (party)
{
FString PartyId = ObtainFromSomeWhere();

party->SetPlayerCustomData(PartyId, PlayerCustomData, [](
const FPgosResult& Ret, const PartyCustomData * Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("OnSetPlayerCustomDataSuccess"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("OnSetPlayerCustomDataFailed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

3.8 Start Matchmaking

Due to design considerations, parties are completely decoupled from matchmaking. We can use multi-player matchmaking requests to pass all player IDs as parameters into the matchmaking request interface. (refer to the detailed guide to Matchmaking)

The leader can start the matchmaking process via the process shown below:

  • Leader starts the matchmaking process through the game UI

  • The leader's client gets each member's player_id and generates a player_id list

  • The leader's client passes the player_id list as the core parameter and calls the StartMatchmaking interface in the Matchmaking service

  • The members' clients will immediately receive a push message indicating that the Leader has started the matchmaking process

  • The members' clients call the JoinMatchmaking interface

  • After matchmaking succeeds, all players will receive a push message, prompting that they can join the battle and connect to the dedicated server

3.9 Join Lobby

Due to design considerations, parties are completely decoupled from the Lobby. We can use the multi-player lobby joining request to pass all player IDs into the lobby interface.

3.10 Party Chat

Party service has built-in chat function and can be used directly: EnableChatting is true when calling the interface UPgosClientAPICreateParty::CreateParty.

  • Any member in the party can send party chat messages
  • Any member in the party can receive party chat messages
  • Members will not receive party chat messages from themselves
sequenceDiagram participant A as PlayerA participant B as PlayerB participant C as PlayerC participant Chat as Party Service A->>Chat:SendPartyChatTextMsg(Message) Chat-->>A:return success(ClientChatMsgInfo) or failed Chat-->>B:OnReceivePartyChatMsg:ChatMsgInfo Chat-->>C:OnReceivePartyChatMsg:ChatMsgInfo

Send party chat message

Call SendPartyChatTextMsg/SendPartyChatCustomMsg from Party module as follows:

#include "PgosSDKCpp.h"

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

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

Receive party chat message

Bind dynamic delegate to OnReceivePartyChatMsg as follows:

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

void SomeUObjectClass::SomeFunction()
{
auto party = IPgosSDKCpp::Get().GetClientPartyAPI();
if (party)
{
party->OnReceivePartyChatMsg().AddUObject(this, &SomeUObjectClass::OnReceiveChatMsg);
}
}

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

3.11 Party Query in Portal

You can query party records in the web portal. When a party is created, the PGOS backend automatically generates and stores a history record that contains the following information:

  • Leader Player Id.
  • Member Player Ids: members may change during the party's lifetime and the information of members will be recorded here.
  • The creation and dismission time of the party (if dismissed).
  • Party Id, Name.
  • Max number of players allowed by the creator.

The query screen is shown below:

Party list:

1605498590504

Party details:

1605498539738

3.12 Party Events

Event NameEvent Description
OnPartyStrategyChangedSent when when party strategies changed.
OnPartyMemberJoinedSent when new players joined the party.
OnPartyMemberLeftSent when players left the party.
OnPartyDismissedSent when the party was dismissed.
OnPartyLeaderTransferredSent when the leader status was transferred.
OnReceivePartyInvitationSent when receive a party invitation.
OnReceivePartyChatMsgSent when party chat message arrived.
OnCustomDataChangedSent when party custom data changed.
OnPartyInfoUpdatedSent when party info updated.
Note

This section discusses a special event OnPartyInfoUpdated, which is triggered when there is a change in party info.

Normally, more specific events occur when changes to party info happen. For example, the OnPartyStrategyChanged event is triggered when party strategies change, and the OnPartyMemberJoined event is triggered when new players join the party.

However, due to network issues or other reasons causing disruptions in the persistent connection, the server might be unable to notify the client of changes in party info (such as players entering or leaving the party) through this connection. If the client remains unaware of changes in party info for an extended period, it could lead to functional abnormalities in the game. To address this issue, the PGOS SDK periodically retrieves the latest party info (typically every 30 to 60 seconds) upon detecting a disruption in the persistent connection, and then notifies the game through the OnPartyInfoUpdated event.

Therefore, it is recommended that games listen to the OnPartyInfoUpdated event in addition to other events below that indicate changes in party info:

  • OnPartyStrategyChanged
  • OnPartyMemberJoined
  • OnPartyMemberLeft
  • OnPartyLeaderTransferred
  • OnCustomDataChanged

4. Key Fields and Data

FieldNotesRemark
Party IdIdentity of partyParty Id is globally unique
NameParty's nameOptional and not unique
Leader Player IdLeader's player id
Max PlayerMax player count
Created TimeTime of party creation
Dismissed TimeTime of party dismission
MembersPlayer Id list of all membersIncluding the leader

5. Key Errors Handling

Error CodeRelevant APIHandling Suggestion
kBackendPartyDuplicatedCreateParty JoinPartyIt means the player are already in other party since a player can only create/join one party concurrently. The game can prompt the player a message like "You should leave the current party and then try again"
kBackendCreatePartyUicCreatePartyIt means the param party name contains profanity word. The game can prompt the player a message like "The party name contains profanity word, you should change it and then try again"
kBackendIsNotPartyLeaderSetGlobalCustomDataIt means the player has no ownership of the party try to set the global custom data of the lobby. Such action is not allowed
kBackendJoinFailedDueToNotFriendOfLeaderJoinPartyIt means the party join_strategy is set to OnlyFriendsOfLeader and the joining player is not a friend of the party leader.
kBackendJoinFailedDueToNotFriendOfmembersJoinPartyIt means the party join_strategy is set to OnlyFriendsOfMembers and the joining player is not a friend of any member of the party.
kBackendJoinFailedDueToNoInvitationJoinPartyIt means the party join_strategy is set to OnlyInvited and the joining player is not invited, or the last invitation has already expired.
The expiration time is internally controlled by the PGOS backend.
kBackendNoJoinPartyPermissionJoinPartyIt usually means the joining player is blocked by the party leader.
Or the joining player has blocked the party leader, it's a two-way relationship.
kBackendPartyNotAllowJoinJoinPartyIt means the party join_strategy is set to NotAllowed, so this party is not allowed to join.
kBackendInviteFailedDueToNotLeaderInvitePlayerToPartyIt means the party invite_strategy is set to OnlyLeaderCanInvite and the inviting player is not the party leader.
kBackendInviteFailedDueToNotAllowJoinInvitePlayerToPartyIt means the party join_strategy is set to NotAllowed, so you can't invite other players to this party.