跳到主要内容

玩家身份认证

1. 概述

本文档详细说明了玩家身份验证的使用方法,这是访问PGOS服务的前提条件。当游戏需要识别玩家身份并管理玩家会话状态时,您需要使用玩家身份验证服务。

完成本教程后,开发人员将了解以下内容:

  • 创建玩家会话的流程。
  • 如何监控玩家会话事件。
  • 如何处理玩家会话异常。
  • 如何使用新账号开始新的玩家会话(切换账号)。

2. 基本流程

2.1 时序图

玩家认证服务包含两个部分:

  • 玩家身份验证
  • 玩家会话状态管理

在使用PGOS服务之前,您需要使用账号服务登录一个身份,然后使用账号提供商返回的open_id和token登录PGOS(创建PGOS玩家会话)。之后,您就可以访问所有PGOS服务。基本流程如下图所示:

sequenceDiagram participant Game Client participant PGOS SDK participant PGOS Backend participant Account Provider Game Client->>Account Provider: Login Account Provider-->>Game Client: return openid and token Game Client->>PGOS SDK: LoginPGOS(openid, token...) PGOS SDK->>PGOS Backend: auth(openid, token...) PGOS Backend->>Account Provider: verify(openid, token) Account Provider-->>PGOS Backend: return verify result PGOS Backend->>PGOS SDK: return auth result PGOS SDK->>Game Client: return success(player_id) or failed(err code/msg) PGOS SDK-->PGOS Backend: maintain and monitor the player-session status PGOS SDK-->>Game Client: invoke event if any player-session status changed Game Client->>PGOS SDK: LogoutPGOS: if no longer needs PGOS PGOS SDK->>PGOS Backend: Logout

2.2 关键字定义

  • 账号提供商:支持玩家登录和认证的账号提供商,常见的账号提供商有PlayFab、INTL、Steam等,游戏一般需要集成其HTTP API或者SDK才能使用其账号服务。 账号提供商有两种类型:

    • 第1类:只支持一个账号平台,以账号提供商Steam为例,其只支持自己的账号平台,即使用账号提供商Steam时只支持Steam账号。

    • 第2类:支持多个账号平台,以账号提供商PlayFab为例,其支持Steam、Google、PSN等账号平台。

  • 账号平台:管理用户账号、身份认证的平台体系,常见的账号平台有Steam、Google、Apple等,一个账号平台可以同时是一个账号提供商,比如Steam。且一个账号提供商可以支持多个账号平台,比如账号提供商 INTL 支持 Steam、Google、XBoxLive 等账号平台。

  • 临时账号服务:提供临时账号服务(FAS),帮助在游戏与真实账号服务对接前,在游戏开发阶段可以使用 FAS 临时替代真实账号服务来访问PGOS服务。

  • open ID :账号服务提供商提供的用户身份标识。

  • token:账号服务提供商提供的 open_id 对应的有效 ticket。

  • player id:PGOS 提供的游戏区服唯一玩家 ID。

  • player session:游戏客户端与 PGOS 后端之间的会话,玩家会话过期后,游戏客户端将无法访问 PGOS 服务。

3. 使用玩家鉴权

3.1 登录 PGOS

在使用账号服务的身份登录 PGOS 之前,您需要确保已启用相应的鉴权插件。之后,您可以调用 PlayerAuth::LoginPGOS API 来登录 PGOS 并创建玩家会话。如果登录成功,您就可以访问所有 PGOS 服务;如果登录失败,请查看错误代码和错误信息以了解发生了什么。

3.1.1 登录参数

FClientLoginPGOSParams 结构参数作为登录过程的输入。

struct FClientLoginPGOSParams: public FBaseBackendEventParams
{
int32 account_provider = -1;
FString account_open_id;
FString account_token;
FString title_region_id;
TMap<FString, FString> extra_param;
};

struct FBaseBackendEventParams
{
/**
* [Optional] A game-defined custom value pass to PGOS backend event.
* If you need to get a custom value in the event, pass it in, otherwise, ignore this field.
* The API parameter structures inherited from this structure all support passing a custom data to the related events.
* The size is limited to 4096 bytes in a narrow-character string, if it is a wide-character string, it should be converted to a utf-8 narrow-character string before calculating its length.
* Detail for PGOS backend event: https://pgos.intlgame.com/pgosdoc/manual/service_manual/extensions/event.html.
* Detail for how to reference the 'event_custom_data':https://pgos.intlgame.com/pgosdoc/manual/service_manual/extensions/virtual_server_v2.html#43-the-request-and-response-protocol.
*/
FString event_custom_data;
};

FClientLoginPGOSParams 结构体中的字段:

  • account_provider: 账号服务提供商,游戏客户端使用其 SDK/API 进行账号登录。默认值为 -1(未设置),此时 PgosSDK 将尝试从配置中读取该值:account_provider
const int32 FAS = 0;                  /** Fake account service provided by PGOS. */
const int32 INTL = 1; /** Account service provided by Proxima for overseas games. */
const int32 MSDK = 2; /** Account service provided by Tencent for China mainland games. */
const int32 WeGame = 3; /** Account service provided by WeGame. */
const int32 PlayFab = 4; /** Account service provided by PlayFab. */
const int32 Steam = 5; /** Account service provided by Steam. */
const int32 Epic = 6; /** Account service provided by Epic(EOS), only epicgames identity is supported. */
const int32 XboxLive = 7; /** Account service provided by XboxLive. */
const int32 PlayStationNetwork = 8; /** Account service provided by PlayStationNetwork. */
const int32 Nintendo = 9; /** Account service provided by Nintendo. */
const int32 JWT = 10; /** Custom JWT-based authentication using your own identity provider. */
  • account_open_id: 已登录账号的 open id。从账号服务获取玩家用户ID

  • account_token: 账号 token。从账号服务获取。

  • title_region_id: 玩家想要登录的游戏区服 ID,可以从 PGOS控制台获取。

  • extra_param: [可选] 账号服务的额外参数,其值取决于用于登录的账号服务。

  • event_custom_data: [可选] 传递给 PGOS 后端事件:event_player_login_pgos 的游戏自定义值。如果需要在事件中获取自定义值,请传入该值,否则忽略此字段。大小限制为 4096 字节(将字符串转换为 utf8 格式后)。点击此处查看如何在云函数中引用 event_custom_data

在接下来的章节中,我们将详细介绍如何使用这些账号提供商登录 PGOS。

3.1.2 登录排队

PgosSDK v0.31.0 或更高版本起,我们提供了登录排队系统,允许玩家逐步进入游戏。此功能默认关闭,您可以在网页端启用。更多详情,请参阅 玩家设置

启用登录排队后,您应该监控 OnIntoLoginQueue 事件,以处理玩家在登录过程中被放入登录排队的情况:

#include "PgosSDKCpp.h"     

void SomeUObjectClass::NativeConstruct()
{
auto playerAuth = IPgosSDKCpp::Get().GetClientPlayerAuthAPI();
if (playerAuth)
{
playerAuth->OnIntoLoginQueue().AddUObject(this, &SomeUObjectClass::OnIntoLoginQueue);
}
}

void SomeUObjectClass::OnIntoLoginQueue(const FPgosClientIntoLoginQueueEvt& Evt)
{
// you may want to show a progress bar here
const auto QueuePosition = Evt.queue_position;
const auto WaitTimeSeconds = Evt.estimated_wait_time;
...
}

另外,您需要监视“OnLoginQueueUpdated”事件来获取登录队列的最新信息:

#include "PgosSDKCpp.h"     

void SomeUObjectClass::NativeConstruct()
{
auto playerAuth = IPgosSDKCpp::Get().GetClientPlayerAuthAPI();
if (playerAuth)
{
playerAuth->OnLoginQueueUpdated().AddUObject(this, &SomeUObjectClass::OnLoginQueueUpdated);

}
}

void SomeUObjectClass::OnLoginQueueUpdated(const FPgosClientLoginQueueUpdatedEvt& Evt)
{
// you can update your progress bar here
const auto QueuePosition = Evt.queue_position;
const auto WaitTimeSeconds = Evt.estimated_wait_time;
...
}

OnLoginQueueUpdated 事件持续触发,直到玩家离开队列或者登录成功。玩家可以通过调用 QuitLoginQueue 退出队列:

#include "PgosSDKCpp.h"     

void SomeUObjectClass::QuitLoginQueue()
{
auto playerAuth = IPgosSDKCpp::Get().GetClientPlayerAuthAPI();
if (playerAuth)
{
playerAuth->QuitLoginQueue(FPgosOnApiResult{});

}
}

并注意,在登录排队中,LoginPGOS 回调不会被触发,直到玩家退出排队 - 通过调用QuitLoginQueue 或到达排队之前时成功登录。

3.1.3 使用FAS登录

在游戏开发阶段,开发者可以使用FAS来测试所有PGOS服务,然后再集成真实的账号服务。以下是示例代码:

#include "PgosSDKCpp.h"

// call after a successful FAS login
// and the FASAuthRet is obtained from FAS Login
void SomeUObjectClass::LoginPGOS(const FClientFasLoginInfo& FASAuthRet)
{
auto playerAuth = IPgosSDKCpp::Get().GetClientPlayerAuthAPI();
if (playerAuth)
{
FClientLoginPGOSParams Params;
Params.account_provider = (int32)EAccountProvider::FAS;
Params.account_open_id = FASAuthRet.open_id;
Params.account_token = FASAuthRet.token;
Params.title_region_id = TEXT("region_id_to_login"); // Obtained from PGOS portal console.
playerAuth->LoginPGOS(Params, [](const FPgosResult& Ret, const FClientAuthenticationInfo* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("OnLoginPGOSSuccess: player_id=%s"), *Data->player_id);
}
else
{
UE_LOG(LogTemp, Log, TEXT("OnLoginPGOSFailed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

以下是使用 FAS 登录时关于 FClientLoginPGOSParams 字段的提示:

  • extra_param:使用 FAS 登录时可忽略此字段。
  • event_custom_data:如果您需要在 PGOS Event: event_player_login_pgos 中获取自定义值,请传入该值,如不需要则可忽略此字段。

3.1.4 使用INTLSDK登录(Player Network)

要使用INTLSDK(Player Network)登录PGOS,您必须先在门户控制台上安装INTLSDK 插件,然后才能开始编码工作,以下是示例代码:

#include "PgosSDKCpp.h"

// call after a successful INTLSDK login
// and the INTLAuthRet is obtained from INTLSDK Login
void SomeUObjectClass::LoginPGOS(const FINTLAuthResult& INTLAuthRet)
{
auto playerAuth = IPgosSDKCpp::Get().GetClientPlayerAuthAPI();
if (playerAuth)
{
FClientLoginPGOSParams Params;
Params.account_provider = (int32)EAccountProvider::INTL;
Params.account_open_id = INTLAuthRet.OpenID;
Params.account_token = INTLAuthRet.Token;
Params.title_region_id = TEXT("region_id_to_login"); // Obtained from PGOS portal console.
Params.extra_param.Add(TEXT("account_channel"), INTLAuthRet.ChannelID);
playerAuth->LoginPGOS(Params, [](const FPgosResult& Ret, const FClientAuthenticationInfo* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("OnLoginPGOSSuccess: player_id=%s"), *Data->player_id);
}
else
{
UE_LOG(LogTemp, Log, TEXT("OnLoginPGOSFailed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

以下是使用 INTL SDK 登录时关于 ClientLoginPGOSParams 字段的提示:

  • extra_param:添加key "account_channel",并将其值设置为从 INTL SDK 认证结果中获取的 ChannelID
Params.extra_param.Add(TEXT("account_channel"), INTLAuthRet.ChannelID);

3.1.5 通过 WeGame 登录

要使用 WeGame 登录 PGOS,您必须先在门户控制台上安装 WeGame 插件,然后才能开始编写代码,以下是示例代码:

#include "PgosSDKCpp.h"

// call after a successful WeGame login
// and the RailId and SessionTicket are obtained from the Rail API 'AsyncAcquireSessionTicket' response
void SomeUObjectClass::LoginPGOS(const FString& RailId, const FString& SessionTicket)
{
auto playerAuth = IPgosSDKCpp::Get().GetClientPlayerAuthAPI();
if (playerAuth)
{
FClientLoginPGOSParams Params;
Params.account_provider = (int32)EAccountProvider::WeGame;
Params.account_open_id = RailId;
Params.account_token = SessionTicket;
Params.title_region_id = TEXT("region_id_to_login"); // Obtained from PGOS portal console.
playerAuth->LoginPGOS(Params, [](const FPgosResult& Ret, const FClientAuthenticationInfo* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("OnLoginPGOSSuccess: player_id=%s"), *Data->player_id);
}
else
{
UE_LOG(LogTemp, Log, TEXT("OnLoginPGOSFailed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

以下是使用WeGame登录时关于ClientLoginPGOSParams字段的提示:

  • extra_param:使用WeGame登录时请忽略此字段。
  • event_custom_data:如果您需要在PGOS事件:event_player_login_pgos中获取自定义值,请传入该值,如果不需要则忽略此字段。

3.1.6 使用 PlayFab 登录

要使用 PlayFab 登录 PGOS,您必须先在门户控制台上安装 PlayFab 插件,然后才能开始编写代码,以下是示例代码:

#include "PgosSDKCpp.h"

// call after a successful PlayFab login
// and the PlayFabId and SessionTicket are obtained from PlayFab Login
void SomeUObjectClass::LoginPGOS(const FString& PlayFabId, const FString& SessionTicket)
{
auto playerAuth = IPgosSDKCpp::Get().GetClientPlayerAuthAPI();
if (playerAuth)
{
FClientLoginPGOSParams Params;
Params.account_provider = (int32)EAccountProvider::PlayFab;
Params.account_open_id = PlayFabId;
Params.account_token = SessionTicket;
Params.title_region_id = TEXT("region_id_to_login"); // Obtained from PGOS portal console.
playerAuth->LoginPGOS(Params, [](const FPgosResult& Ret, const FClientAuthenticationInfo* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("OnLoginPGOSSuccess: player_id=%s"), *Data->player_id);
}
else
{
UE_LOG(LogTemp, Log, TEXT("OnLoginPGOSFailed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

以下是使用 PlayFab 登录时关于 ClientLoginPGOSParams 字段的提示:

  • extra_param:使用 PlayFab 登录时请忽略此字段。
  • event_custom_data:如果您需要在 PGOS Event: event_player_login_pgos 中获取自定义值,请传入该值,如果不需要则忽略此字段。

3.1.7 通过 Steam 登录

要使用 Steam 登录 PGOS,您必须先在门户控制台上安装 Steam 插件,然后才能开始您的编码工作,以下是示例代码:

#include "PgosSDKCpp.h"

// call after a successful Steam login
// and the SteamId and SessionTicket are obtained from the steamworks API 'ISteamUser::GetAuthSessionTicket'
void SomeUObjectClass::LoginPGOS(const FString& SteamId, const FString& SessionTicket)
{
auto playerAuth = IPgosSDKCpp::Get().GetClientPlayerAuthAPI();
if (playerAuth)
{
FClientLoginPGOSParams Params;
Params.account_provider = (int32)EAccountProvider::Steam;
Params.account_open_id = SteamId;
Params.account_token = SessionTicket;
Params.title_region_id = TEXT("region_id_to_login"); // Obtained from PGOS portal console.
playerAuth->LoginPGOS(Params, [](const FPgosResult& Ret, const FClientAuthenticationInfo* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("OnLoginPGOSSuccess: player_id=%s"), *Data->player_id);
}
else
{
UE_LOG(LogTemp, Log, TEXT("OnLoginPGOSFailed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

以下是使用Steam登录时关于ClientLoginPGOSParams字段的提示:

  • extra_param:使用Steam登录时请忽略此字段。
  • event_custom_data:如果您需要在PGOS事件:event_player_login_pgos中获取自定义值,请传入该值,如果不需要则忽略此字段。

3.1.8 使用 Epic 登录

目前,PGOS 仅支持 epic games 身份验证,不支持 EOS 上的其他身份提供商。要使用 Epic 登录 PGOS,您必须先在门户控制台上安装 Epic 插件,然后才能开始编写代码,以下是示例代码:

#include "PgosSDKCpp.h"

// call after login to Epic
void SomeUObjectClass::LoginPGOS(const FString& EpicAccountId, const FString& JWT_IDToken)
{
auto playerAuth = IPgosSDKCpp::Get().GetClientPlayerAuthAPI();
if (playerAuth)
{
FClientLoginPGOSParams Params;
Params.account_provider = (int32)EAccountProvider::Epic;
Params.account_open_id = EpicAccountId;
Params.account_token = JWT_IDToken;
Params.title_region_id = TEXT("region_id_to_login"); // Obtained from PGOS portal console.
playerAuth->LoginPGOS(Params, [](const FPgosResult& Ret, const FClientAuthenticationInfo* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("OnLoginPGOSSuccess: player_id=%s"), *Data->player_id);
}
else
{
UE_LOG(LogTemp, Log, TEXT("OnLoginPGOSFailed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

以下是使用Epic登录时关于ClientLoginPGOSParams字段的提示:

  • extra_param:使用Steam登录时请忽略此字段。
  • event_custom_data:如果您需要在PGOS事件:event_player_login_pgos中获取自定义值,请传入该值,如果不需要则可以忽略此字段。

3.1.9 使用 XboxLive 登录

要使用 XboxLive 登录 PGOS,您必须先在控制台上安装 XboxLive 插件,然后才能开始编码工作,以下是示例代码:

#include "PgosSDKCpp.h"

// call after login to XboxLive
// The endpoint URL should be "https://xboxlive.com/" when obtaining an XSTSToken by calling `XUserGetTokenAndSignatureAsync`.
void SomeUObjectClass::LoginPGOS(const FString& Xuid, const FString& XSTSToken)
{
auto playerAuth = IPgosSDKCpp::Get().GetClientPlayerAuthAPI();
if (playerAuth)
{
FClientLoginPGOSParams Params;
Params.account_provider = (int32)EAccountProvider::XboxLive;
Params.account_open_id = Xuid;
Params.account_token = XSTSToken;
Params.title_region_id = TEXT("region_id_to_login"); // Obtained from PGOS portal console.
playerAuth->LoginPGOS(Params, [](const FPgosResult& Ret, const FClientAuthenticationInfo* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("OnLoginPGOSSuccess: player_id=%s"), *Data->player_id);
}
else
{
UE_LOG(LogTemp, Log, TEXT("OnLoginPGOSFailed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

以下是使用 XboxLive 登录时关于 ClientLoginPGOSParams 字段的提示:

  • extra_param:使用 XboxLive 登录时请忽略此字段。
  • event_custom_data:如果您需要在 PGOS Event: event_player_login_pgos 中获取自定义值,请传入该值,如果不需要则忽略此字段。

3.1.10 使用 PlayStationNetwork 登录

要使用 PlayStationNetwork 登录 PGOS,您必须先在门户控制台上安装 PlayStationNetwork 插件,然后才能开始编码工作,以下是示例代码:

#include "PgosSDKCpp.h"

// call after login to PlayStationNetwork
void SomeUObjectClass::LoginPGOS(const FString& UserId, const FString& AuthorizationCode)
{
auto playerAuth = IPgosSDKCpp::Get().GetClientPlayerAuthAPI();
if (playerAuth)
{
FClientLoginPGOSParams Params;
Params.account_provider = (int32)EAccountProvider::PlayStationNetwork;
Params.account_open_id = UserId;
Params.account_token = AuthorizationCode;
Params.title_region_id = TEXT("region_id_to_login"); // Obtained from PGOS portal console.
playerAuth->LoginPGOS(Params, [](const FPgosResult& Ret, const FClientAuthenticationInfo* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("OnLoginPGOSSuccess: player_id=%s"), *Data->player_id);
}
else
{
UE_LOG(LogTemp, Log, TEXT("OnLoginPGOSFailed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

以下是使用 PlayStationNetwork 登录时关于 ClientLoginPGOSParams 字段的提示:

  • extra_param:使用 PlayStationNetwork 登录时请忽略此字段。
  • event_custom_data:如果您需要在 PGOS Event: event_player_login_pgos 中获取自定义值,请传入该值,如果不需要则忽略此字段。

3.1.11 使用Nintendo登录

要使用Nintendo登录PGOS,您必须先在门户控制台上安装Nintendo 插件,然后才能开始编码工作,以下是示例代码:

#include "PgosSDKCpp.h"

// call after login to Nintendo
void SomeUObjectClass::LoginPGOS(const FString& Naid, const FString& NsaIdToken)
{
auto playerAuth = IPgosSDKCpp::Get().GetClientPlayerAuthAPI();
if (playerAuth)
{
FClientLoginPGOSParams Params;
Params.account_provider = (int32)EAccountProvider::Nintendo;
Params.account_open_id = Naid;
Params.account_token = NsaIdToken;
Params.title_region_id = TEXT("region_id_to_login"); // Obtained from PGOS portal console.
playerAuth->LoginPGOS(Params, [](const FPgosResult& Ret, const FClientAuthenticationInfo* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("OnLoginPGOSSuccess: player_id=%s"), *Data->player_id);
}
else
{
UE_LOG(LogTemp, Log, TEXT("OnLoginPGOSFailed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

以下是使用任天堂账号登录时关于ClientLoginPGOSParams字段的提示:

  • extra_param:使用任天堂账号登录时请忽略此字段。
  • event_custom_data:如果您需要在PGOS事件:event_player_login_pgos中获取自定义值,请传入该值,如果不需要则忽略此字段。

3.1.12 使用 JWT 登录

JWT 配置

要使用 JWT 登录 PGOS,您必须先在门户控制台上安装 JWT 插件,然后在门户控制台上配置以下设置:

配置项说明
Allowed AlgorithmsJWT 验证允许的签名算法(如 RS256、ES256、HS256)。
Expected Issuer[可选] JWT 中预期的 iss(签发者)声明值。
Expected Audience[可选] JWT 中预期的 aud(受众)声明值。
Shared Secrets[可选] 用于 HMAC 算法(HS256/HS384/HS512)的对称密钥。
Public Keys[可选] 用于 RSA/ECDSA 算法的 PEM 格式非对称公钥。

示例代码

#include "PgosSDKCpp.h"

// 从您的身份提供商获取 JWT 令牌后调用
void SomeUObjectClass::LoginPGOS(const FString& UserId, const FString& JwtToken)
{
auto playerAuth = IPgosSDKCpp::Get().GetClientPlayerAuthAPI();
if (playerAuth)
{
FClientLoginPGOSParams Params;
Params.account_provider = (int32)EAccountProvider::JWT;
Params.account_open_id = UserId; // 如果启用了 require_sub,必须与 JWT 中的 'sub' 声明匹配
Params.account_token = JwtToken; // 完整的 JWT 令牌字符串
Params.title_region_id = TEXT("region_id_to_login"); // 从 PGOS 门户控制台获取
playerAuth->LoginPGOS(Params, [](const FPgosResult& Ret, const FClientAuthenticationInfo* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("OnLoginPGOSSuccess: player_id=%s"), *Data->player_id);
}
else
{
UE_LOG(LogTemp, Log, TEXT("OnLoginPGOSFailed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

以下是使用 JWT 登录时关于 ClientLoginPGOSParams 字段的提示:

  • account_open_id:用户标识符。如果 JWT 配置中启用了 require_sub,此值必须与 JWT 令牌中的 sub 声明匹配。
  • account_token:完整的 JWT 令牌字符串,格式为 header.payload.signature
  • extra_param:使用 JWT 登录时请忽略此字段。
  • event_custom_data:如果您需要在 PGOS 事件:event_player_login_pgos 中获取自定义值,请传入该值,如不需要则可忽略此字段。

3.2 登出PGOS

当游戏客户端想要退出或不再需要访问PGOS服务时,开发者应调用LogoutPGOS来结束玩家会话。

FClientLogoutPGOSParams结构参数作为登出过程的输入。

struct FClientLogoutPGOSParams 
{
FString event_custom_data;
};

FClientLogoutPGOSParams 结构中的字段:

  • event_custom_data[可选] 传递给 PGOS 后端事件event_player_logout_pgos 的游戏自定义值。如果您需要在事件中获取自定义值,请传入该值,否则可以忽略此字段。大小限制为 4096 字节(将字符串转换为 utf8 格式后)。点击此处查看如何在云函数中引用 event_custom_data

以下是登出 PGOS 的示例代码:

#include "PgosSDKCpp.h"

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

3.3 切换账号

如果玩家想要切换并登录新账号,请按照以下流程操作:

  • 调用 LogoutPGOS 函数终止当前玩家会话。
  • 通过账号服务切换至新账号。
  • 调用 LoginPGOS 函数创建新的玩家会话。

3.4 监听玩家会话变更事件

在玩家会话的生命周期内可能会触发一些事件。开发者需要了解这些事件并正确处理。要监听玩家会话事件,请按以下方式将动态委托绑定到OnPlayerSessionChanged

#include "PgosSDKCpp.h"

void SomeUObjectClass::MonitorPlayerSessionEvent()
{
auto playerAuth = IPgosSDKCpp::Get().GetClientPlayerAuthAPI();
if (playerAuth)
{
playerAuth->OnPlayerSessionChanged().AddUObject(this, &SomeUObjectClass::OnPlayerSessionChanged);
}
}

void SomeUObjectClass::OnPlayerSessionChanged(const FClientPlayerSessionChangedEvt& event)
{
/*
struct FClientPlayerSessionChangedEvt{
EClientPlayerSessionEvt evt;
FString msg;
}
*/
UE_LOG(LogTemp, Log, TEXT("OnPlayerSessionChanged"));
}

PGOS中定义了6种玩家会话事件,这些事件会在玩家会话状态发生变化时触发:

enum class EClientPlayerSessionEvt : uint8
{
Dummy = 0,
SessionExpired_NetworkAbnormal = 1,
SessionExpired_TokenExpired = 2,
SessionExpired_KickoutByGame = 3,
SessionExpired_KickoutByBan = 4,
SessionExpired_KickoutByAnotherLogin = 5,
SessionExpired_KickoutByTitleRegionClosed = 6,
SessionExpired_KickoutByAccountCanceled = 7,
SessionStart = 8,
SessionEnd = 9,
};
  • Type: SessionExpired_NetworkAbnormal: 网络异常时触发。建议游戏客户端静默调用ReLoginPGOS API 创建新的玩家会话以重新获得对 PGOS 服务的访问权限。如果 ReLoginPGOS API 仍然失败,则通知玩家检查本地网络环境。

  • Type: SessionExpired_TokenExpired: 当 PGOS PLAYER TOKEN 过期时会触发。这通常发生在:游戏客户端长时间断网(例如,App 在移动设备上进入后台模式),然后重新获得网络(App 进入前台模式)并重新与 PGOS 连接。建议游戏客户端默默调用 ReLoginPGOS API 创建新的玩家会话以重新获得对 PGOS 服务的访问权限。如果 ReLoginPGOS API 仍然失败,则通知玩家使用账号服务重新登录。

  • Type: SessionExpired_KickoutByGame: 当游戏行为将玩家踢出时(通过 virtual_server API:KickOutPlayer())将会触发。当这种情况发生时,建议通知玩家检查账号状态。 本段末尾有关于如何处理玩家被踢出的情况的建议。

  • Type: SessionExpired_KickoutByBan:当玩家被 virtual_server API:BanPlayer(kickout=true) 封禁时将会触发,点击查看封禁 事件.当发生这种情况时,建议通知玩家检查账户状态。 本段末尾有关于如何处理玩家被踢出的情况的建议。

  • Type: SessionExpired_KickoutByAnotherLogin:当账户执行另一次登录(可能是在另一台设备上)时会触发。 当发生这种情况时,建议通知玩家检查账户状态。 本段末尾有关于如何处理玩家被踢出的情况的建议

  • Type: SessionExpired_KickoutByTitleRegionClosed: 在 关闭游戏区服的工作流程中,如果设置为踢出玩家,那么在指定的时间,玩家将会离线并收到此事件 。当这种情况发生时,建议通知玩家游戏区服已关闭,玩家可以找到可以访问的游戏区服登录或者直接退出游戏。

  • Type: SessionExpired_KickoutByAccountCanceled:当登录账号被取消时会触发。当这种情况发生时,建议通知玩家检查账号状态。 本段末尾有关于如何处理玩家被踢出的情况的建议

  • Type: SessionEnd:当玩家会话结束时会触发。一旦玩家会话过期,游戏客户端将无法访问 PGOS 服务。

提示

处理玩家被踢出时,通常有两种方式:

  • 立即退出游戏:由于游戏客户端无法访问 PGOS 服务,可能无法提供完整的游戏功能,因此游戏可以在通知玩家后退出游戏客户端。
  • 如果玩家正在进行战斗,等待战斗结束后再退出游戏:虽然当前游戏客户端无法访问 PGOS 服务,但承载战斗会话的 DS 不受影响,因此当前玩家仍然可以完成当前战斗,等待战斗结束后再退出游戏会是一种相对友好的方式。(注意:依赖 PGOS 服务的功能,如文字聊天,在当前游戏客户端将无法继续使用。)

3.5 监控玩家封禁事件

在玩家会话期间可能会触发玩家封禁事件,开发者可以通过调用虚拟服务器API:BanPlayer()来触发封禁事件。如果BanPlayer()的布尔参数kickout设置为true,还会触发PlayerSessionChangedEvt (SessionExpired_KickoutByBan)事件。开发者需要注意此事件并妥善处理。要监控玩家封禁事件,请按以下方式绑定动态委托到OnPlayerBanned

#include "PgosSDKCpp.h"

void SomeUObjectClass::MonitorPlayerBannedEvent()
{
auto playerAuth = IPgosSDKCpp::Get().GetClientPlayerAuthAPI();
if (playerAuth)
{
playerAuth->OnPlayerBanned().AddUObject(this, &SomeUObjectClass::OnPlayerBanned);
}
}

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

开发者可以通过 FClientPlayerBannedEvt 获取封禁详情:

struct FClientPlayerBannedEvt{
/** Reason for ban. */
FString ban_reason;
/** Ban creation time. */
int64 ban_created_time;
/** Ban duration */
int64 ban_duration;
/** Ban expiration time, 0 means forever */
int64 ban_expired_time;
}
  • ban_expired_time 表示封禁结束的时间,如果为0则表示永久封禁。

  • ban_duration 表示封禁的持续时间。如果是永久封禁,其值为0。

被封禁后,在调用 LoginPGOS 时会在回调中返回 FClientAuthBanInfo,类似于 FClientPlayerBannedEvt

struct FClientAuthBanInfo{
/** Whether the player is banned. */
bool is_banned = false;
/** Reason for ban. */
FString ban_reason;
/** Ban creation time. */
int64 ban_created_time;
/** Ban duration */
int64 ban_duration;
/** Ban expiration time, 0 means forever */
int64 ban_expired_time;
}

3.6 监控游戏区服关闭事件

游戏可以根据需要关闭游戏区服,您可以在游戏区服开启与关闭了解更多信息。当游戏区服关闭时,玩家将无法进行新的登录、新的匹配或新的战斗会话分配。开发者可以将动态委托绑定到OnTitleRegionClosed来监控该事件:

#include "PgosSDKCpp.h"

void SomeUObjectClass::MonitorRegionClosedEvent()
{
auto playerAuth = IPgosSDKCpp::Get().GetClientPlayerAuthAPI();
if (playerAuth)
{
playerAuth->OnTitleRegionClosed().AddUObject(this, &SomeUObjectClass::OnRegionClosed);
}
}

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

该事件的相关信息如下:

struct FClientTitleRegionClosedEvt 
{
/** The current UTC timestamp of the backend server. (in seconds) */
int64 current_time = 0;
/**
* The approximate Unix timestamp at which the player will be kicked offline. (in seconds)
* If the value is 0, the PGOS backend will kick the player offline soon.
* If the value is -1, the PGOS backend will still allow the current player to remain online, and battle sessions the player is in will not be affected.
*/
int64 kicked_offline_time = 0;
};

由于OnTitleRegionClosed事件是针对所有在线玩家的事件,出于性能考虑,该事件通过持久连接心跳的方式通知到玩家客户端,心跳间隔为每30秒一次。需要特别注意的是,OnTitleRegionClosed事件的推送行为会根据Close Title Region操作的不同而有所不同。

image-20240912151511610

  • 存在3种场景:

    • 选择不踢下线选项:

    玩家客户端将在下一次持久连接心跳中收到OnTitleRegionClosed事件,且FClientTitleRegionClosedEvtkicked_offline_time字段值为-1

    • 选择踢下线选项:

    • 关闭开始后0分钟:

    设置为0会使PGOS后端立即将玩家踢下线,因此持久连接将断开,玩家客户端可能没有机会通过心跳接收到OnTitleRegionClosed事件。如果幸运地收到该事件,kicked_offline_time字段值将为0

    • 关闭开始后n分钟(n > 0):

    玩家客户端将在下一次持久连接心跳中收到OnTitleRegionClosed事件。

    无论如何,当玩家被踢下线时,都会触发一个原因为SessionExpired_KickoutByTitleRegionClosedPlayerSessionChangedEvt事件。

3.7 监控事件通道状态变更事件

事件通道作为数据管道,用于PGOS后端向客户端发送事件通知(如聊天消息/好友请求)。当事件通道状态发生变化时,将触发该事件。事件通道状态可能受到API调用(如登录/登出)和网络环境波动等因素的影响。在某些情况下,该事件有助于游戏/玩家了解PGOS/游戏服务质量下降的可能原因。例如,当事件通道不可用时,游戏客户端可以在适当的位置向玩家显示消息,如:"聊天功能暂时不可用,正在维护中..."。

开发者可以绑定动态委托到OnEventChannelStatusChanged来监控该事件:

#include "PgosSDKCpp.h"

void SomeUObjectClass::MonitorEventChannelStatusChanged()
{
auto playerAuth = IPgosSDKCpp::Get().GetClientPlayerAuthAPI();
if (playerAuth)
{
playerAuth->OnEventChannelStatusChanged().AddUObject(this, &SomeUObjectClass::OnEventChannelStatusChanged);
}
}

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

开发者可以通过 FClientEventChannelStatusChangedEvt 获取事件详情:

struct FClientEventChannelStatusChangedEvt 
{
/** Indicate the current status of the event channel. */
EClientPgosEventChannelStatus current_status = EClientPgosEventChannelStatus::Dummy;
/** Indicate the status of the event channel in the previous StatusChanged event. If there is no previous event (meaning it is the first event), its value is set to 'Dummy'. */
EClientPgosEventChannelStatus last_status = EClientPgosEventChannelStatus::Dummy;
};

enum class EClientPgosEventChannelStatus : uint8
{
/** Invalid status. */
Dummy = 0,
/** When the client is connecting or reconnecting to the backend in order to establish the channel, the status is set to 'Creating'. */
Creating = 1,
/** When the channel is successfully established, the status is set to 'Available', indicating that the event notification service is functioning properly. */
Available = 2,
/**
* When the client expectedly(logout) or unexpectedly(network exception) disconnects from the backend, the status is set to 'Unavailable', and the event notification service stops working.*/
Unavailable = 3
};
提示

当事件通道的状态变为Unavailable时,PGOS事件通知服务将停止工作。这意味着游戏客户端将无法接收聊天消息、好友请求以及其他依赖后端推送通知的事件。如果这不是预期的状态(例如,玩家已登出),PgosSDK将自动尝试重建通道,直到成功为止,重试频率会逐渐降低。最初每3秒尝试重建一次,最终频率会降低到每5分钟一次。

3.8 重新登录 PGOS

ReLoginPGOS API 将尝试使用内存中缓存的 LoginPGOSParams 重新登录 PGOS,如果执行成功,将创建一个新的玩家会话并触发 PlayerSessionEvt::SessionStart 事件。建议在发生 PlayerSessionEvt::SessionExpired_NetworkAbnormalPlayerSessionEvt::SessionExpired_TokenExpired 事件时调用此 API。

以下是示例代码:

#include "PgosSDKCpp.h"

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

3.9 游戏服务器上的玩家验证

在某些场景下,游戏服务器可能需要验证来自游戏客户端的玩家身份。PGOS提供以下方法来帮助游戏完成这项工作:

sequenceDiagram participant Game Client participant PGOS Client SDK participant Game Server participant PGOS HTTP API Backend Game Client->>PGOS Client SDK: get MyAuthTicket PGOS Client SDK->>Game Client: auth_ticket string Game Client-->>Game Server: connect with: player_id, auth_ticket Game Server->>PGOS HTTP API Backend: HTTP API: VerifyAuthTicket(player id, auth_ticket) PGOS HTTP API Backend-->>Game Server: The player's identity is valid or not
  • 相关 API:
    • 相关客户端 SDK API:FPgosPlayerAuthAPI::MyAuthTicket
/**
* Get the current player's PGOS authenticated ticket which can be used to authenticate the player via the HTTP Backed API.
* The ticket has a reliable validity period of 60 seconds. Please retrieve it from SDK when needed and avoid caching it.
* If the player is not logged in, an empty string will be returned.
*
* @return The current player's ticket, valid after a successful LoginPGOS.
*/
FString MyAuthTicket() const;

4. 使用FAS账号服务

我们提供临时账号服务(FAS)来帮助您在集成真实账号服务之前体验所有PGOS服务。默认情况下,FAS仅在DevTest的Title Region可用。如果您需要在Prod Title Region使用FAS(例如,在Prod Title Region进行内部测试),则必须在Web门户上配置FAS白名单

4.1 FAS编程指南

真实账号服务会包含许多功能,而FAS账号服务仅提供一些简单的关键功能:

APIDescription
Login使用指定的账号登录。
AutoLogin使用本地历史账号登录(如果没有历史账号,系统将自动创建一个)。

4.1.1 Login Client API

玩家可以使用指定的 FAS 账号登录,FAS 账号无需提前注册,可以是任意不包含换行符('\r'、'\n')的字符串。FAS 账号可由游戏工作室进行管理。以下是示例代码:

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

void SomeUObjectClass::SomeFunction()
{
auto FakeAccount = IPgosSDKCpp::Get().GetClientFakeAccountAPI();
if (FakeAccount)
{
FString Account = TEXT("any_string_as_account"); // account can be any string without line breaks ('\r', '\n').
FakeAccount->Login(Account, [](const FPgosResult& Ret, const FClientFasLoginInfo* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("Login Success"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("Login Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

4.1.2 AutoLogin Client API

生成的临时账号将保存在本地,并可在登录时自动使用。临时账号保存在文件 (/fas/account) 中,如果您想使用此临时账号登录其他设备,可以分发或替换该文件。以下是示例代码:

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

void SomeUObjectClass::SomeFunction()
{
auto FakeAccount = IPgosSDKCpp::Get().GetClientFakeAccountAPI();
if (FakeAccount)
{
FakeAccount->AutoLogin([](const FPgosResult& Ret, const FClientFasLoginInfo* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("AutoLogin Success"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("AutoLogin Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}
提示

您可以使用任意未注册的FAS账户登录Dev 和 Test Title Region,因此,您也可以选择跳过FAS登录,直接调用LoginPGOS API来登录这些Title Region。

4.2 配置FAS白名单

出于安全考虑,Prod Title Region对FAS访问进行了限制——仅允许FAS白名单中的FAS账户登录。

配置步骤:

步骤 1: 登录网页门户,进入您的Prod Title Region,然后前往 Players > Settings > FAS Account:

image-20260112212757805

步骤 2: 点击 Add Account 按钮,即可自动生成指定数量的FAS白名单账户。

image-20260112213008299

步骤 3: 调整FAS账户(可选):您可以按需修改FAS账户,但为确保安全,请配置足够复杂的账户Token。

image-20260112213637864

现在,您可以跳过 FAS 登录,直接使用列入白名单的 FAS OpenID 和 Token 登录PGOS

作为一个可选的工作,您可以导出白名单中的FAS账户,这样就可以执行一些程序化的事情,例如生成可供测试客户端读取的FAS 白名单配置。

提示

重要提示:

  • 只有白名单中配置的open_idtoken组合才能使用FAS登录Prod Title Region。
  • 如果未配置白名单或账户不在白名单中,登录将失败,并显示错误代码kBackendFasNotInWhitelist (10116)
  • 此功能专为内部QA测试、或生产环境中的实时调试等场景而设计。

5. 关键错误处理

Error Code相关 APIHandling Suggestion
kBackendYouAreBanned (10101)LoginPGOS ReLoginPGOS这表示玩家已被封禁。游戏可以向玩家显示提示消息,如"您已被封禁,解封时间为xxxx"
kBackendAuthClientSecretIsNull (10002)LoginPGOS ReLoginPGOS这表示客户端密钥配置不存在。您需要使用门户网站创建一个客户端密钥(如果客户端密钥配置已存在,请联系 pgos 团队)。
kBackendAddonAuthCfgIsNull (10040)LoginPGOS ReLoginPGOS这表示当前账户的认证方式不受支持。您需要确保已启用相应的认证插件
kBackendFasNotAllowedLoginProd (10028)LoginPGOS ReLoginPGOS禁止在production region使用FAS。
kBackendFasNotInWhitelist (10116)LoginPGOS (FAS)FAS 账号不在 Release 类型大区的白名单中。请在门户网站上配置 FAS 白名单或使用已加入白名单的账号。
kBackendTitleRegionIsClosed (54)LoginPGOS ReLoginPGOS该游戏区服已关闭登录,请在门户网站上查看游戏区服状态。
kBackendGeoRestricted (10102)LoginPGOS ReLoginPGOS由于地理位置限制导致登录失败。请检查门户网站上的地理位置限制设置。
kCltSdkPlayerNotLoginPgos (210000)ReLoginPGOS当前玩家尚未登录PGOS,因此"重新登录"将无法生效。
kCltSdkPlayerAlreadyLoginPgos (210001)LoginPGOS ReLoginPGOS当前玩家已登录PGOS,此操作不适用。
kSdkLastOperationNotCompleted (200011)LoginPGOS ReLoginPGOS当前玩家已登录PGOS,此操作不适用。
kBackendJwtTokenInvalid (10120)LoginPGOS (JWT)JWT 令牌格式无效。请确保令牌遵循 header.payload.signature 格式。
kBackendJwtSignatureInvalid (10121)LoginPGOS (JWT)JWT 签名验证失败。请检查是否配置了正确的签名密钥,以及令牌是否被篡改。
kBackendJwtTokenExpired (10122)LoginPGOS (JWT)JWT 令牌已过期。请从您的身份提供商获取新的令牌。
kBackendJwtTokenNotYetValid (10123)LoginPGOS (JWT)JWT 令牌尚未生效(nbf 声明)。令牌的 nbf 时间尚未到达。
kBackendJwtAlgorithmNotAllowed (10125)LoginPGOS (JWT)JWT 签名算法不在允许列表中。请检查门户网站上的 allowed_algorithms 配置。
kBackendJwtKeyNotFound (10127)LoginPGOS (JWT)未找到与 JWT 匹配的验证密钥。请确保已配置匹配 kid 的密钥或可通过 JWKS 端点获取。
kBackendJwtKidMissing (10138)LoginPGOS (JWT)JWT 的 kid 头部是必需的但缺失。请确保您的身份提供商在 JWT 头部中包含 kid 字段。
kBackendJwtExpMissing (10139)LoginPGOS (JWT)JWT 的 exp 声明是必需的但缺失。请确保您的身份提供商在令牌中包含 exp 声明。
kBackendJwtSubMissing (10130)LoginPGOS (JWT)JWT 的 sub 声明是必需的但缺失。请确保您的身份提供商在令牌中包含 sub 声明。
kBackendJwtSubMismatch (10131)LoginPGOS (JWT)JWT 的 sub 声明不合法。请检查 account_open_id 参数是否与 JWT 中的 sub 声明匹配。
kBackendJwtIssMismatch (10132)LoginPGOS (JWT)JWT 的 iss 声明不合法。请检查门户网站上的 expected_issuer 配置。
kBackendJwtAudMismatch (10133)LoginPGOS (JWT)JWT 的 aud 声明不合法。请检查门户网站上的 expected_audience 配置。
kBackendJwtTokenReplay (10134)LoginPGOS (JWT)JWT 令牌已被使用过(检测到重放攻击)。每个带有 jti 声明的 JWT 只能使用一次。
kSdkNetworkError (200008)All Network API网络错误,请查看错误信息了解详情。