组队
1. 概述
组队 是为玩家创建即时队伍的服务,玩家可以通过它组建 “临时虚拟队伍”,然后发起战斗。包括组队创建、玩家邀请、自定义数据、 组队 事件调度等功能。 组队中有一名队长和多名队员,所有玩家都有创建组队的权限。创建队伍后,创建者默认为队长,但必要时可以将队长身份转给其他队员。由于组队是即时队伍,玩家手动或离线退出组队后,将被移出组队 。
2. 架构图
游戏通过 PGOS 客户端 SDK 访问组队服务,包含两种交互,一种是通过 HTTPS 协议进行客户端调用,另一种是客户端事件 ,是 PGOS 后端的推送服务推送的消息,通过 PGOS 客户端 SDK 鉴权后自动建立并维护的 WebSocket 长连接实现,所有事件都由 SDK 以推送消息的形式触发,并通知游戏。
3. 核心功能和规则
3.1 创建组队
任何玩家都可以创建组队,但一名玩家同时只能创建/加入一个组队。
从Party模块调用CreateParty
,如下所示:
- UE C++
- UE BP
#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);
}
});
}
}
#include "PgosClientPartyAPI.h"
void SomeUObjectClass::CreateParty()
{
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;
PGOS_CALL_ASYNC_API(UPgosClientAPICreateParty::CreateParty,
this,
&SomeUObjectClass::OnCreatePartySuccess,
&SomeUObjectClass::OnCreatePartyFailed,
Params);
}
void SomeUObjectClass::OnCreatePartySuccess(
FPgosResult Result, FClientPartyInfo PartyInfo)
{
UE_LOG(LogTemp, Log, TEXT("OnCreatePartySuccess"));
}
void SomeUObjectClass::OnCreatePartyFailed(
FPgosResult Result, FClientPartyInfo PartyInfo)
{
UE_LOG(LogTemp, Error, TEXT("OnCreatePartyFailed: err_code=%d, err_msg=%s"), Result.err_code, *Result.msg);
}
3.2 组队邀请和加入控制
在上述CreateParty
代码中,有两个参数join_strategy
和invite_strategy
,用于控制组队如何处理"加入"和"邀请"。
join_strategy控制其他玩家如何加入组队,有四种不同的策略:
EClientPartyJoinStrategy | Description |
---|---|
Public | 除了被队长屏蔽的玩家外,任何玩家都可以加入组队。 这是默认设置。 |
OnlyFriendsOfLeader | 仅允许队长的好友加入组队。 |
OnlyFriendsOfMembers | 只有未被队长屏蔽的任意队员的好友才能加入组队。 |
OnlyInvited | 只有被队长明确邀请且未被其屏蔽的玩家才能加入组队。 |
invite_strategy 控制组队成员如何邀请其他人。
EClientPartyInviteStrategy | Description |
---|---|
AllMembersCanInvite | 组队中的所有成员都可以邀请其他玩家加入组队。 被队长或邀请者屏蔽的玩家无法被邀请。 这是默认设置。 |
OnlyLeaderCanInvite | 只有队长可以邀请其他玩家加入组队。 被队长屏蔽的玩家无法被邀请。 |
组队创建后,只有队长
可以更改加入策略
和邀请策略
。 请按以下方式调用Party模块中的SetPartyStrategy
:
- UE C++
- UE BP
#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);
}
});
}
}
#include "PgosClientPartyAPI.h"
void SomeUObjectClass::SetPartyStrategy()
{
FClientSetPartyStrategyParams Params;
Params.party_id = PartyId;
Params.join_strategy = EClientPartyJoinStrategy::Public;
Params.invite_strategy = EClientPartyInviteStrategy::AllMembersCanInvite;
PGOS_CALL_ASYNC_API(UPgosClientAPISetPartyStrategy::SetPartyStrategy,
this,
&SomeUObjectClass::OnSetPartyStrategySuccess,
&SomeUObjectClass::OnSetPartyStrategyFailed,
Params);
}
void SomeUObjectClass::OnSetPartyStrategySuccess(
FPgosResult Result)
{
UE_LOG(LogTemp, Log, TEXT("OnSetPartyStrategySuccess"));
}
void SomeUObjectClass::OnSetPartyStrategyFailed(
FPgosResult Result)
{
UE_LOG(LogTemp, Error, TEXT("OnSetPartyStrategyFailed: err_code=%d, err_msg=%s"), Result.err_code, *Result.msg);
}
所有成员(除了队长)将收到PartyStrategy
变更的通知。请按以下方式绑定动态委托到OnPartyStrategyChanged
:
- UE C++
- UE BP
#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.
}
#include "PgosClientEventDispatcher.h"
// Observe the notification of party invitation somewhere
void SomeUObjectClass::SomeFunction()
{
auto pDispatcher = UPgosClientEventDispatcher::GetPgosClientEventDispatcher();
if (pDispatcher)
{
pDispatcher->OnPartyStrategyChanged.Clear();
// add delegate callback
pDispatcher->OnPartyStrategyChanged.AddDynamic(
this,
&APluginTestGameModeBase::OnPartyStrategyChanged);
}
}
// Handle the invitation
void APluginTestGameModeBase::OnPartyStrategyChanged(const FClientPartyStrategyChangedEvt& event)
{
UE_LOG(LogTemp, Log, TEXT("OnPartyStrategyChanged"));
// Party members cannot change `join_strategy` and `invite_strategy` actively.
}
3.3 邀请和加入
队长和队员可以邀请其他玩家加入组队,每位玩家同一时间只能加入一个队伍。
3.3.1 邀请
从组队模块调用InvitePlayerToParty
,具体如下:
- UE C++
- UE BP
#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);
}
});
}
}
#include "PgosClientPartyAPI.h"
void SomeUObjectClass::InvitePlayerToParty()
{
FString PartyId;
TArray<FString> InviteePlayerIds;
PGOS_CALL_ASYNC_API(UPgosClientAPIInvitePlayerToParty::InvitePlayerToParty,
this,
&SomeUObjectClass::OnInvitePlayerToPartySuccess,
&SomeUObjectClass::OnInvitePlayerToPartyFailed,
PartyId,
InviteePlayerIds);
}
void SomeUObjectClass::OnInvitePlayerToPartySuccess(
FPgosResult Result)
{
UE_LOG(LogTemp, Log, TEXT("OnInvitePlayerToPartySuccess"));
}
void SomeUObjectClass::OnInvitePlayerToPartyFailed(
FPgosResult Result)
{
UE_LOG(LogTemp, Error, TEXT("OnInvitePlayerToPartyFailed: err_code=%d, err_msg=%s"), Result.err_code, *Result.msg);
}
按照以下方式将动态委托绑定到 OnReceivePartyInvitation
:
- UE C++
- UE BP
#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.
}
#include "PgosClientEventDispatcher.h"
// Observe the notification of party invitation somewhere
void SomeUObjectClass::SomeFunction()
{
auto pDispatcher = UPgosClientEventDispatcher::GetPgosClientEventDispatcher();
if (pDispatcher)
{
pDispatcher->OnReceivePartyInvitation.Clear();
// add delegate callback
pDispatcher->OnReceivePartyInvitation.AddDynamic(
this,
&APluginTestGameModeBase::OnReceivePartyInvitation);
}
}
// Handle the invitation
void APluginTestGameModeBase::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 加入组队
按如下方式从 Party 模块调用 JoinParty
:
- UE C++
- UE BP
#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);
}
});
}
}
#include "PgosClientPartyAPI.h"
void SomeUObjectClass::JoinParty()
{
FString PartyId;
PGOS_CALL_ASYNC_API(UPgosClientAPIJoinParty::JoinParty,
this,
&SomeUObjectClass::OnJoinPartySuccess,
&SomeUObjectClass::OnJoinPartyFailed,
PartyId);
}
void SomeUObjectClass::OnJoinPartySuccess(
FPgosResult Result, FClientPartyInfo PartyInfo)
{
UE_LOG(LogTemp, Log, TEXT("OnJoinPartySuccess"));
}
void SomeUObjectClass::OnJoinPartyFailed(
FPgosResult Result, FClientPartyInfo PartyInfo)
{
UE_LOG(LogTemp, Error, TEXT("OnJoinPartyFailed: err_code=%d, err_msg=%s"), Result.err_code, *Result.msg);
}
3.4 队长特权
- 踢出成员:队长可以随时踢出任何成员。
- 解散组队。
- 将队长转让给其他成员。
从 Party 模块调用
KickPartyMember/DismissParty/TransferPartyLeader
方法,如下所示:
- UE C++
- UE BP
#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);
}
});
}
}
#include "PgosClientPartyAPI.h"
// Kick out members
void SomeUObjectClass::KickPartyMember()
{
FString PartyId = ObtainFromSomeWhere();
FString KickPlayerID = ObtainFromSomeWhere();
PGOS_CALL_ASYNC_API(UPgosClientAPIKickPartyMember::KickPartyMember,
this,
&APluginTestGameModeBase::OnKickMemberSuccess,
&APluginTestGameModeBase::OnKickMemberFailed,
PartyId,
KickPlayerID);
}
void SomeUObjectClass::OnKickMemberSuccess(FPgosResult Result)
{
UE_LOG(LogTemp, Log, TEXT("OnKickMemberSuccess"));
}
void SomeUObjectClass::OnCreatePartyFailed(FPgosResult Result)
{
UE_LOG(LogTemp, Error, TEXT("OnKickMemberFailed: err_code=%d, err_msg=%s"), Result.err_code, *Result.msg);
}
// Dismiss the party
void SomeUObjectClass::DismissParty()
{
FString PartyId = ObtainFromSomeWhere();
PGOS_CALL_ASYNC_API(UPgosClientAPIDismissParty::DismissParty,
this,
&APluginTestGameModeBase::OnDismissPartySuccess,
&APluginTestGameModeBase::OnDismissPartyFailed,
PartyId);
}
void SomeUObjectClass::OnDismissPartySuccess(FPgosResult Result)
{
UE_LOG(LogTemp, Log, TEXT("OnDismissPartySuccess"));
}
void SomeUObjectClass::OnDismissPartyFailed(FPgosResult Result)
{
UE_LOG(LogTemp, Error, TEXT("OnDismissPartyFailed: err_code=%d, err_msg=%s"), Result.err_code, *Result.msg);
}
// Transfer leader privilege to another member
void SomeUObjectClass::TransferPartyLeader()
{
FString PartyId = ObtainFromSomeWhere();
FString NewLeaderPlayerId = ObtainFromSomeWhere();
PGOS_CALL_ASYNC_API(UPgosClientAPITransferPartyLeader::TransferPartyLeader,
this,
&APluginTestGameModeBase::OnTransferLeaderSuccess,
&APluginTestGameModeBase::OnTransferLeaderFailed,
PartyId,
NewLeaderPlayerId);
}
void SomeUObjectClass::OnTransferLeaderSuccess(FPgosResult Result)
{
UE_LOG(LogTemp, Log, TEXT("OnTransferLeaderSuccess"));
}
void SomeUObjectClass::OnTransferLeaderFailed(FPgosResult Result)
{
UE_LOG(LogTemp, Error, TEXT("OnTransferLeaderFailed: err_code=%d, err_msg=%s"), Result.err_code, *Result.msg);
}
3.5 解散组队
在以下情况下组队将被解散:
队长解散组队。
当所有成员都离开组队或登出游戏后,组队将自动解散。
sequenceDiagram PlayerA->>PartyService: dismiss PartyService-->>PlayerB: notify dismissed PartyService-->>PlayerC: notify dismissed
请参考上一节的示例代码。
3.6 退出组队
- 任何玩家都可以退出组队。当队长退出组队时,PGOS 会自动将另一名成员提升为新的队长。
- 玩家登出后将自动退出组队。
按照以下方式调用 Party 模块中的 LeaveParty
:
- UE C++
- UE BP
#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);
}
});
}
}
#include "PgosClientPartyAPI.h"
// Leave the party
void SomeUObjectClass::LeaveParty()
{
FString PartyId = ObtainFromSomeWhere();
PGOS_CALL_ASYNC_API(UPgosClientAPILeaveParty::LeaveParty,
this,
&APluginTestGameModeBase::OnLeavePartySuccess,
&APluginTestGameModeBase::OnLeavePartyFailed,
PartyId);
}
void SomeUObjectClass::OnLeavePartySuccess(FPgosResult Result)
{
UE_LOG(LogTemp, Log, TEXT("OnLeavePartySuccess"));
}
void SomeUObjectClass::OnLeavePartyFailed(FPgosResult Result)
{
UE_LOG(LogTemp, Error, TEXT("OnLeavePartyFailed: err_code=%d, err_msg=%s"), Result.err_code, *Result.msg);
}
3.7 自定义数据处理
组队自定义数据用于帮助游戏扩展组队功能,例如"组队准备就绪"、"组队投票"等。每个组队都有两层自定义数据:全局自定义数据
和玩家自定义数据
。游戏开发者可以在其中存储任何数据来实现特定功能。
全局自定义数据
是一个字符串,用于存储全局范围的数据。所有成员都可以访问,但在游戏客户端中只有组队队长有权限存储和修改。
玩家自定义数据
是一个字符串到字符串(玩家ID -> 数据)的映射,用于存储每个组队成员的数据,其中键是玩家ID,值是由该成员设置的数据。
所有成员都可以访问,但玩家只能存储和修改自己的玩家自定义数据。 当任何自定义数据发生变化时(包括全局自定义数据和玩家自定义数据),
所有成员都会在游戏客户端收到PartyCustomDataChangedEvt
事件。
调用 GetPartyInfo
或 CurrentParty
来获取包含 PartyCustomData 的 PartyInfo。
调用 SetGlobalCustomData
来设置组队的全局自定义数据。请注意,只有队长有权限进行此操作:
- UE C++
- UE BP
#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);
}
});
}
}
#include "PgosClientPartyAPI.h"
void SomeUObjectClass::SetGlobalCustomData()
{
FString PartyId = ObtainFromSomeWhere();
PGOS_CALL_ASYNC_API(UPgosClientAPIGetCustomData::SetGlobalCustomData,
this,
&APluginTestGameModeBase::OnSetGlobalCustomDataSuccess,
&APluginTestGameModeBase::OnSetGlobalCustomDataFailed,
PartyId,
GlobalCustomData);
}
void SomeUObjectClass::OnGSetGlobalCustomDataSuccess(FPgosResult& Result, FPartyCustomData& Data)
{
UE_LOG(LogTemp, Log, TEXT("OnSetGlobalCustomDataSuccess"));
}
void SomeUObjectClass::OnSetGlobalCustomDataFailed(FPgosResult& Result, FPartyCustomData& Data)
{
UE_LOG(LogTemp, Error, TEXT("OnSetGlobalCustomDataFailed: err_code=%d, err_msg=%s"), Result.err_code, *Result.msg);
}
调用 SetPlayerCustomData
来设置玩家自定义数据。请注意,玩家只有权限存储和修改自己的玩家自定义数据:
- UE C++
- UE BP
#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);
}
});
}
}
#include "PgosClientPartyAPI.h"
void SomeUObjectClass::SetPlayerCustomData()
{
FString PartyId = ObtainFromSomeWhere();
PGOS_CALL_ASYNC_API(UPgosClientAPIGetCustomData::SetPlayerCustomData,
this,
&APluginTestGameModeBase::OnSetPlayerCustomDataSuccess,
&APluginTestGameModeBase::OnSetPlayerCustomDataFailed,
PartyId,
PlayerCustomData);
}
void SomeUObjectClass::OnSetPlayerCustomDataSuccess(FPgosResult& Result, FPartyCustomData& Data)
{
UE_LOG(LogTemp, Log, TEXT("OnSetPlayerCustomDataSuccess"));
}
void SomeUObjectClass::OnSetPlayerCustomDataFailed(FPgosResult& Result, FPartyCustomData& Data)
{
UE_LOG(LogTemp, Error, TEXT("OnSetPlayerCustomDataFailed: err_code=%d, err_msg=%s"), Result.err_code, *Result.msg);
}
Key Error Codes
3.8 开始匹配
由于设计上的考虑,队伍和匹配是完全解耦的,我们可以使用多人匹配请求,将所有玩家ID作为参数传入匹配请求接口。([参考匹配详细指南](./ 匹配 .md)) Leader可以通过以下流程开始匹配 :
- Leader通过游戏UI启动匹配
- 领队客户端获取各个成员的player_id,并生成player_id列表
- 领队客户端将player_id列表作为核心参数传入,调用匹配服务中的StartMatchmaking接口
- 成员客户端会立刻收到领队已启动匹配推送消息
- 成员客户端调用JoinMatchmaking接口
- 匹配成功后,所有玩家都会收到推送消息,提示可以加入战斗并连接专属服务器
3.9 加入房间
由于设计上的考虑,队伍和房间是完全解耦的,我们可以使用多人加入房间请求将所有玩家ID传入房间接口。
3.10 组队聊天
组队服务内置聊天功能,可直接使用:调用接口 UPgosClientAPICreateParty::CreateParty
时 EnableChatting
为 true。
- 组队中任何成员均可发送组队聊天消息
- 组队中任何成员均可接收组队聊天消息
- 成员不会收到自己发送的组队聊天消息
发送组队聊天消息
从组队模块调用SendPartyChatTextMsg
/SendPartyChatCustomMsg
,具体如下:
#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);
}
});
}
}
接收组队聊天消息
将动态委托绑定到OnReceivePartyChatMsg
,具体如下:
#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 在门户网站中查询组队
您可以在网页门户中查询组队记录。当创建组队时,PGOS后端会自动生成并存储一条历史记录,其中包含以下信息:
队长玩家ID
成员玩家ID:成员可能在组队存在期间发生变化,成员信息将记录在此
组队的创建时间和解散时间(如果已解散)
组队ID、名称
创建者设定的最大允许玩家数量
查询界面如下所示:组队列表
组队详情
3.12 组队事件
Event Name | 事件描述 |
---|---|
OnPartyStrategyChanged | 当组队策略改变时发送。 |
OnPartyMemberJoined | 当新玩家加入组队时发送。 |
OnPartyMemberLeft | 当玩家离开组队时发送。 |
OnPartyDismissed | 当组队解散时发送。 |
OnPartyLeaderTransferred | 当领导者状态被转移时发送。 |
OnReceivePartyInvitation | 当收到组队邀请时发送。 |
OnReceivePartyChatMsg | 当收到组队聊天消息时发送。 |
OnCustomDataChanged | 当组队自定义数据发生变更时发送。 |
OnPartyInfoUpdated | 当组队信息更新时发送。 |
本节讨论一个特殊的事件 OnPartyInfoUpdated
,当组队信息发生变化时会触发该事件。
通常,当组队信息发生变化时,会触发更具体的事件。例如,当组队策略改变时会触发 OnPartyStrategyChanged
事件,当新玩家加入组队时会触发 OnPartyMemberJoined
事件。
然而,由于网络问题或其他原因导致持久连接中断,服务器可能无法通过此连接通知客户端组队信息的变化(如玩家进入或离开组队)。如果客户端长时间无法感知组队信息的变化,可能会导致游戏功能异常。为了解决这个问题,PGOS SDK 在检测到持久连接中断时会定期获取最新的组队信息(通常是每30到60秒),然后通过 OnPartyInfoUpdated
事件通知游戏。
因此,建议游戏除了监听以下表示组队信息变化的事件外,还要监听 OnPartyInfoUpdated
事件:
OnPartyStrategyChanged
OnPartyMemberJoined
OnPartyMemberLeft
OnPartyLeaderTransferred
OnCustomDataChanged
4. Key Fields and Data
字段 | 说明 | Remark |
---|---|---|
Party Id | 组队ID | Party Id 全局唯一 |
Name | 队伍名字 | 可选且不唯一 |
Leader Player Id | Leader的 玩家ID | |
Max Player | 最大玩家数 | |
Created Time | 组队创建时间 | |
Dismissed Time | 组队解散事件 | |
Members | 所有组队成员的ID列表 | 包括 leader |
5. Key Errors Handling
Error Code | 相关 API | 处理建议 |
---|---|---|
kBackendPartyDuplicated | CreateParty JoinParty | 这表示玩家已经在其他组队中,因为一个玩家同时只能创建/加入一个组队。游戏可以向玩家提示类似"请先退出当前组队后再试"的信息 |
kBackendCreatePartyUic | CreateParty | 这表示参数 party name 包含不当用语。游戏可以向玩家提示类似"组队名称包含不当用语,请修改后重试"的信息 |
kBackendIsNotPartyLeader | SetGlobalCustomData | 这表示该玩家不是组队的所有者却试图设置房间的全局自定义数据。此类操作是不被允许的 |
kBackendJoinFailedDueToNotFriendOfLeader | JoinParty | 这表示组队的 join_strategy 设置为 OnlyFriendsOfLeader ,而尝试加入的玩家不是队长的好友。 |
kBackendJoinFailedDueToNotFriendOfmembers | JoinParty | 这表示组队的 join_strategy 设置为 OnlyFriendsOfMembers ,而尝试加入的玩家与组队中的任何成员都不是好友关系。 |
kBackendJoinFailedDueToNoInvitation | JoinParty | 这表示组队的 join_strategy 被设置为 OnlyInvited (仅限邀请),而加入的玩家未被邀请,或者最后一次邀请已经过期。过期时间由 PGOS 后台系统内部控制。 |
kBackendNoJoinPartyPermission | JoinParty | 这通常意味着加入的玩家被组队队长屏蔽了。 或者是加入的玩家屏蔽了组队队长,这是一个双向的关系。 |
kBackendPartyNotAllowJoin | JoinParty | 这表示组队的 join_strategy 设置为 NotAllowed ,因此不允许加入此组队。 |
kBackendInviteFailedDueToNotLeader | InvitePlayerToParty | 这表示组队的 invite_strategy 设置为 OnlyLeaderCanInvite ,而发出邀请的玩家不是队长。 |
kBackendInviteFailedDueToNotAllowJoin | InvitePlayerToParty | 这表示组队的 join_strategy 被设置为 NotAllowed ,因此您无法邀请其他玩家加入此组队。 |