跳到主要内容

Battle Session

1. 什么是Battle Session

Battle Session是战斗服务的基础。Battle Session实例可以通过 Lobby / Matchmaking / World 等服务或PGOS HTTP API创建,并通过DS Hosting服务放置在在线或本地DS上。Battle Session还管理游戏中的玩家和DS访问信息等数据。

NOTE

tip 注意 PGOS允许玩家同时加入多个Battle Session。游戏客户端可以使用API DescribeBattleSessions查询玩家参与的Battle Session。

2. Battle Session的生命周期

2.1 创建

PGOS中创建Battle Session主要有两种方式:

  1. 通过 Lobby / Matchmaking / World 等战斗服务:
    • Lobby所有者调用StartBattle接口后,Lobby服务会向Battle Session管理服务请求创建Battle Session
    • Matchmaking服务在完成一组匹配票的聚合后,会向Battle Session管理服务请求创建Battle Session
    • World会根据玩家职责向Battle Session管理服务请求创建Battle Session
  2. 通过PGOS HTTP API StartBattleSessionPlacement。可以不通过战斗服务,直接向Battle Session Management服务申请Battle Session

完整的Battle Session创建流程,请参考DS Hosting服务的DS Hosting integration文档。

NOTE

注意 Battle Session Management是PGOS内部服务,不向用户开放。但了解该服务的存在有助于用户理解Battle Service的工作机制。

2.2 激活

Battle Session实例创建后,会被交给可用的DS资源(可以是运行在CVM上的在线DS进程,或是运行在开发者本地机器上的本地DS进程)。DS可以选择立即激活Battle Session,或在完成迎接玩家的准备后激活。

在PGOS中,这个过程称为Battle Session放置。Battle Session被激活的事件会通过PGOS的Lobby / Matchmaking / World 模块中的事件通知游戏客户端,告知DS已准备就绪。

PGOS将Battle Session的状态变更推送给游戏中的所有成员,让客户端知道运行游戏的DS可以连接,具体是在Battle Session被激活时。

2.3 终止

一旦Battle Session被终止,就无法重新启用(激活)。目前有几种终止Battle Session的方式:

  • DS可以随时通过调用ProcessEnding接口销毁其持有的Battle Session。建议在执行此操作前妥善处理游戏中的玩家。
  • 运维人员可以在PGOS Portal中手动终止正在进行的Battle Session。通常在异常情况下作为紧急措施,或在开发和调试期间处理那些未按计划终止的Battle Session。
  • World服务支持自动回收空闲的Battle Session(即没有玩家的Battle Session)。

2.4 状态转换

stateDiagram-v2 [*] --> Pending: 开始Battle Session放置 Pending --> Active: DS调用ActivateBattleSesssion() Pending --> Timeout: 未调用ActivateBattleSesssion() Active --> Terminated: Battle Session结束 Terminated --> [*] Timeout --> [*]
  • Pending到Active:DS可以通过调用ActivateBattleSession接口触发此状态转换
  • Pending到Timeout:如果DS在限定时间内没有调用ActivateBattleSession,将触发此状态转换
  • Active到Terminated:DS可以通过调用ProcessEnding接口触发此状态转换

3. Player Battle Session

Battle Session为每个分配到战斗的玩家维护一个会话,我们称之为Player Battle Session。这些会话在游戏DS激活Battle Session时立即初始化,当玩家多次分配到同一个Battle Session时会有所不同。

Player Battle Session的状态转换:

stateDiagram-v2 [*] --> Pending Pending --> Reserved: ReservePBS [*] --> Reserved Pending --> Completed: RemovePBS Reserved --> Active: AcceptPBS Reserved --> Completed: RemovePBS Active --> Completed: RemovePBS Active --> Disconnected: DisconnectPBS Disconnected --> Active: AcceptPBS Disconnected --> Completed: RemovePBS Completed --> [*]
  • Pending:此状态仅用于World服务。当玩家请求加入锁定的Battle Session时,会分配一个Pending状态的Battle Session,以便DS可以进一步处理其加入请求。
    • DS可以调用ReservedPlayerBattleSession接口为玩家在游戏中保留座位
    • DS也可以调用RemovePlayerBattleSession将玩家从Battle Session中移除
  • Reserved:当DS激活Battle Session时,所有Player Battle Session变为Reserved
  • ReservedActive:只有DS可以通过调用AcceptPlayerBattleSession接口触发此状态转换。游戏可以使用此接口与PGOS后端验证玩家身份的合法性。我们建议DS将此作为处理玩家连接请求的必要步骤。
  • Pending/Reserved/Active/DisconnectedCompleted:DS可以通过调用RemovePlayerBattleSession接口结束Player Battle Session。客户端也可以调用TerminatedPlayerBattleSession接口结束Player Battle Session。一旦pbs达到Completed状态,对其调用AcceptPlayerBattleSession接口将返回失败。
  • ActiveDisconnected:DS可以通过调用DisconnectPlayerBattleSession接口触发此状态转换。Disconnect状态仅用于帮助游戏标记玩家此时已断开连接,与玩家和DS之间的实际连接状态无关。
  • DisconnectedActive:DS可以通过调用AcceptPlayerBattleSession接口触发此状态转换。

4. 在Portal中检查Battle Session

4.1 从Battle Session日志查询Battle Session

打开网页门户 (web portal),选择 Battle (战斗) 菜单下的子菜单 Battle Session (Battle Session)。您可以使用以下参数搜索Battle Session:

  • Fleet (舰队): Battle Session所在的 DS 舰队。
  • Config (配置): 产生Battle Session的匹配/大厅配置。
  • ID: 指定一个Battle Session ID。Battle Session ID 可以从客户端的匹配结果或 DS 端的 FDelegateOnStartBattleSession 事件中获取。
  • Status (状态): Battle Session的状态。
  • Created Time (创建时间): Battle Session的创建时间。

image-20220607194347829

  • Export Searched Result (导出搜索结果):您可以将搜索结果导出到 CSV 文件,PGOS 最多支持导出 5000 条数据。如果数据量大于 5000,则只导出前 5000 条数据。
  • View Battle Session Details (查看Battle Session详情):在Battle Session搜索结果中点击 Battle Session ID 可以查看详细信息。

4.2 在指定Fleet上查询Battle Session

另一种查询指定Fleet下Battle Session的方法是:

打开网页门户,选择 DS & Builds 菜单下的子菜单 Fleets,然后选择 Battle Sessions (Battle Session) 标签页。

1631517935782

在Battle Session搜索结果中点击 Battle Session ID 可以查看详细信息。

4.3 Player Battle Session

Battle Session为每个被分配到对战中的玩家维护一个Player Battle Session。这些玩家Battle Session在游戏 DS 激活Battle Session时立即初始化。

注意:我们为每个Player Battle Session维护了一套数据,以帮助您跟踪对战中的玩家。此信息可以在Battle Session的详情页面中查看。

1631519416863

  • Player Battle Session ID:分配给Player Battle Session的标识符。
  • Player : 玩家的显示名称。点击可查看玩家的详细信息。
  • Status : Player Battle Session状态。请阅读此处了解如何在游戏 DS 上管理Player Battle Session。
  • Reserved : Player Battle Session已被预留,但玩家尚未连接。
  • Active: 表示玩家已连接到游戏 DS。
  • Completed : Player Battle Session结束。表示玩家不再连接。
  • Disconnected :表示玩家已从游戏 DS 断开连接。
  • Uptime: 玩家在Player Battle Session中处于活跃状态的总时长。
  • Connect Time: 指示玩家连接到游戏服务器的日期时间。
  • Terminate Time: 指示玩家从游戏服务器断开连接的日期时间。

5. 使用PGOS API访问Battle Session

5.1 使用客户端SDK访问Battle Session

您可以使用以下接口在游戏客户端中访问、查询和管理玩家参与的Battle Session。

接口描述
DescribePlayerBattleSession[已弃用!!!]声明已弃用,考虑使用DescribeBattleSessions代替
DescribeBattleSessions获取玩家正在参与的Battle Session列表
TerminatePlayerBattleSession尝试终止玩家的Battle Session。调用成功后,玩家将无法再进入此Battle Session,请谨慎使用

UPgosBattleAPI::DescribeBattleSessions

此接口返回当前玩家的活动Player Battle Session。同时返回游戏DS的访问信息。如果您的客户端意外关闭并希望重新连接到之前的战斗,可以调用此接口获取必要的访问信息。

接口原型:

/**
* 获取玩家的Battle Session信息。
*
* @param PlayerId 玩家ID
*/
void DescribeBattleSessions(
void DescribeBattleSessions(
TFunction<void(const FPgosResult& Ret, const FClientPlayerBattleSessionInfo * Data)> Callback) const;

示例代码:

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

void SomeUObjectClass::SomeFunction()
{
auto Battle = IPgosSDKCpp::Get().GetClientBattleAPI();
if (Battle)
{
FString PlayerId;
Battle->DescribeBattleSessions([](const FPgosResult& Ret, const DescribeBattleSessionsResult * Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("DescribeBattleSessions 成功"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("DescribeBattleSessions 失败: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

UPgosBattleAPI::TerminatePlayerBattleSession

此接口将强制将Player Battle Session的状态更改为Completed。调用此接口将触发PGOS Server SDK的回调:UPgosHostingAPI::OnPlayerBattleSessionsTermianted,通知游戏DS这些已离开Battle Session的玩家。

接口原型:

/**
* 尝试终止玩家的Battle Session。
* 调用成功后,玩家将无法再进入此Battle Session,请谨慎使用
*
* @param BattleSessionId Battle Session ID
* @param PlayerBattleSessionId Player Battle Session ID
*/
void TerminatePlayerBattleSession(
const FString& BattleSessionId,
const FString& PlayerBattleSessionId,
TFunction<void(const FPgosResult& Ret)> Callback) const;

示例代码:

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

void SomeUObjectClass::SomeFunction()
{
auto Battle = IPgosSDKCpp::Get().GetClientBattleAPI();
if (Battle)
{
FString BattleSessionId;
FString PlayerBattleSessionId;
Battle->TerminatePlayerBattleSession(BattleSessionId, PlayerBattleSessionId, [](const FPgosResult& Ret) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("TerminatePlayerBattleSession 成功"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("TerminatePlayerBattleSession 失败: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

5.2 使用服务器SDK访问Battle Session

游戏服务器需要注意以下与Battle Session管理相关的核心事件:

事件描述
OnHealthCheckHealthCheck事件每分钟触发一次,获取游戏进程的健康状态
OnStartBattleSession当Battle Session被放置到服务器时触发StartBattleSession事件。游戏服务器需要在5分钟内调用ActivateBattleSession接口激活Battle Session,否则Battle Session将被放弃
OnBattleSessionUpdated此事件用于在两种特定场景(World和Matchmaking回填)中通知新玩家加入Battle Session
OnProcessTerminate当进程被终止时触发此事件。当发生收缩或有人手动终止Battle Session时触发ProcessTerminate事件
OnPlayerBattleSessionsTerminated此事件定期调用,通知游戏DS那些已从客户端终止的Player Battle Session

以下接口可用于管理Battle Session和Battle Session中的玩家:

接口描述
ActivateBattleSession此API将Battle Session状态转为Active。向PGOS发出信号,表示服务器进程现在已准备好接收玩家会话。应在所有Battle Session初始化完成后调用一次
ProcessEnding向PGOS发出信号表示进程正在结束,此进程上运行的Battle Session也将以Completed状态终止
ReservePlayerBattleSession调用此API将Player Battle Session状态从Pending转为Reserved
AcceptPlayerBattleSession验证玩家会话,并向PGOS发出信号更新玩家会话状态为Active。当客户端请求连接到服务器时应调用此方法
DisconnectPlayerBattleSession将Player Battle Session标记为已断开连接。当玩家非永久性地从DS断开连接时可以调用此接口。可以通过再次调用AcceptPlayerBattleSession接口随时恢复会话
RemovePlayerBattleSession此接口将玩家会话设置为最终状态(Completed)且无法恢复。当玩家永久性地从DS断开连接时应调用此接口
DescribePlayerBattleSessions查询Battle Session的最新玩家

5.3 使用HTTP API访问Battle Session

PGOS提供了几个与战斗管理相关的HTTP API,帮助在不集成Lobby/Matchmaking/World等服务的情况下发起Battle Session放置请求。在PGOS中,这种访问模式称为DS Hosting Standalone模式。

有关DS Hosting Standalone的详细信息,请阅读以下文档:

相关HTTP API请参考以下文档:

6. 管理Battle Session中玩家的建议

本章列出了玩家离开Battle Session的几种典型场景。旨在帮助您理解Player Battle Session的管理。请在开发过程中遵循游戏机制。

PGOS严格限制每个玩家在同一时间只能有一个活动的Battle Session,因此了解Battle Session中Player Battle Session的管理有助于您按照预期为玩家提供在线游戏体验。

6.1 完全由DS管理

即Player Battle Session的状态完全由游戏DS管理。意味着在游戏DS调用RemovePlayerBattleSession接口之前,玩家不能进入任何其他Battle Session。

此解决方案适用于想要严格限制玩家退出正在进行的Battle Session的游戏,但它也会对玩家体验产生负面影响:玩家似乎无论如何都失去了一些自由。

使用此解决方案的一般游戏流程如下:

  • 玩家从游戏DS断开连接后立即从Battle Session中移除
sequenceDiagram participant DS2 participant Client participant DS participant PGOS ServerSDK Note left of Client: 玩家加入Battle Session Client->>DS: 连接 DS->>PGOS ServerSDK: AcceptPlayerBattleSession Note left of Client: 玩家断开连接 DS-->>Client: 断开连接 DS->>PGOS ServerSDK: RemovePlayerBattleSession Note left of Client:玩家可以加入新的Battle Session Client->>DS2: 连接
  • 为玩家保留Player Battle Session直到Battle Session结束
sequenceDiagram participant Client participant DS participant PGOS ServerSDK Note left of Client: 玩家加入Battle Session Client->>DS: 连接 DS->>PGOS ServerSDK: AcceptPlayerBattleSession Note left of Client: 玩家断开连接 DS-->>Client: 断开连接 DS->>PGOS ServerSDK: DisconnectPlayerBattleSession Note left of Client:玩家重新连接到DS Client->>DS: 连接 DS->>PGOS ServerSDK: AcceptPlayerBattleSession Note left of DS: 战斗结束 DS->>PGOS ServerSDK: 通过调用RemovePlayerBattleSession移除所有玩家

6.2 允许客户端离开Battle Session

在此解决方案中,玩家可以通过在客户端调用TerminatePlayerBattleSession接口离开正在进行的Battle Session。无论客户端当时是否已登录游戏DS。

将离开正在进行的Battle Session的权利交给玩家可以防止玩家被糟糕的战斗阻挡,但会对战斗中的其他玩家产生负面影响。利弊需要开发者权衡。

使用此解决方案的一般游戏流程如下:

sequenceDiagram participant DS2 participant Client participant DS participant PGOS ServerSDK Note left of Client: 玩家加入Battle Session Client->>DS: 连接 DS->>PGOS ServerSDK: AcceptPlayerBattleSession Note left of Client: 玩家断开连接 DS-->>Client: 断开连接 DS->>PGOS ServerSDK: DisconnectPlayerBattleSession Note left of Client:玩家可以离开战斗 Client->>Client: TermaintePlayerBattleSession(player_id_x) PGOS ServerSDK->>DS:OnPlayerBattleSessionTerminated[player_id_x] Note left of Client:玩家可以加入新的Battle Session Client->>DS2: 连接

6.3 为玩家检查正在进行的Battle Session

我们强烈建议您在游戏客户端启动后调用DescribePlayerBattleSession接口,确认玩家是否有正在进行的Battle Session信息。因为如果有正在进行的Battle Session且玩家的Player Battle Session不是Completed,玩家将无法进入其他战斗。如果是这样,玩家大致有两种选择:

  • 通过调用TerminatePlayerBattleSession接口离开Battle Session,然后探索更多乐趣
sequenceDiagram participant PGOS ClientSDK participant Client participant DS Client->>PGOS ClientSDK: 客户端启动 Client->>PGOS ClientSDK: DescribePlayerBattleSession Note left of Client:离开当前战斗 Client->>PGOS ClientSDK: TerminatePlayerBattleSession
  • 重新连接到DS以继续正在进行的Battle Session
sequenceDiagram participant PGOS ClientSDK participant Client participant DS participant PGOS ServerSDK Client->>PGOS ClientSDK: 客户端启动 Client->>PGOS ClientSDK: DescribePlayerBattleSession Note left of Client:继续当前战斗 Client->>DS: 连接 DS->>PGOS ServerSDK: AcceptPlayerBattleSession

7. Battle Data

Battle Data旨在构建可从游戏客户端访问的战斗历史信息。游戏可以使用Battle Data自定义其战斗历史功能。

相关文档:此处有Battle Data的典型用例。

7.1 集成步骤

游戏可以通过 PGOS ServerSDK 和 PGOS Client SDK 访问Battle Data服务。

通过 PGOS Server SDK,游戏可以:

  • 以键值对格式为指定的Battle Session构建Battle Data。
  • 为Battle Data设置字符串格式的标签。

通过 PGOS Client SDK,游戏可以:

  • 查询指定玩家的Battle Data列表,并按标签和键进行过滤。
  • 查询指定Battle Session ID 的Battle Data,并按键进行过滤。

1605606131176

7.2 概念

  • 对战数据 (Battle Data)。 与一个对战会话 ID 绑定的一组键值对持久化数据,通常用于记录战斗属性信息、玩家信息、战斗统计信息等数据。
  • 对战记录 (Battle Record)。 PGOS 保存了玩家参与过的所有对战会话 ID。即使游戏没有写入任何对战数据,仍然可以通过 PGOS 提供的接口获取玩家的对战会话信息。
  • 简要信息与详细信息 (Brief & Detail)。 我们使用 'brief' 和 'detail' 作为保留键来存储一场对战的 简要信息与详细信息。提供了一组包装接口来访问这些键。当然,这些接口是可选的,游戏可以构建自己的对战数据来处理复杂场景。在包装接口中,我们抽象了以下两个概念:简要信息 (Brief)。 键 'brief' 对应的字符串值。通常,我们建议游戏使用 简要信息来存储简短且高度简洁的信息,例如获胜队伍、开始时间、击杀/死亡比率等。详细信息 (Detail)。 键 'detail' 对应的字符串值。通常,我们建议游戏使用 详细信息来存储长而复杂的信息,例如每个玩家的统计数据。如果 简要信息足够游戏使用,则 详细信息不是必须的

概念图:

1646121114802

Remark

Battle Data的键或值不支持二进制字符串。

7.3 构建基于键值对的Battle Data

Remark

仅限PGOS Server SDK.

使用战斗数据服务为Battle Session构建Battle Data。

7.3.1 通过调用UpdateBattleData向战斗的Battle Data添加键值对信息

  • BattleSessionID:Battle Session ID。
  • RecordData:自定义键值对。

Battle模块调用UpdateBattleData如下:

#include "PgosSDKCpp.h"

void SomeUObjectClass::UpdateBattleData()
{
auto battle = IPgosSDKCpp::Get().GetServerBattleAPI();
if (battle)
{
FString BattleSessionID;
TMap<FString, FString> RecordData;

battle->UpdateBattleData(BattleSessionID, RecordData, [](const FPgosResult& Ret) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("UpdateBattleData成功"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("UpdateBattleData失败: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

7.3.2 通过调用DeleteBattleData从战斗的Battle Data中移除键

  • BattleSessionID:Battle Session ID。
  • Keys:要移除的战斗数据键。

Battle模块调用DeleteBattleData如下:

#include "PgosSDKCpp.h"

void SomeUObjectClass::DeleteBattleData()
{
auto battle = IPgosSDKCpp::Get().GetServerBattleAPI();
if (battle)
{
FString BattleSessionID;
TArray<FString> Keys;

battle->DeleteBattleData(BattleSessionID, Keys, [](const FPgosResult& Ret, const FServerDelBattleDataResult* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("DeleteBattleData成功"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("DeleteBattleData失败: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

7.4 设置战斗标签

Remark

仅限PGOS Server SDK

通过调用为Battle Session设置标签。

  • BattleSessionID:要构建战斗历史记录的Battle Session ID。
  • Tags:战斗历史记录的标签。主要用于从游戏客户端查询战斗历史记录时筛选项目。

Battle模块调用UpdateBattleRecord如下:

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

void SomeUObjectClass::UpdateBattleRecord()
{
auto battle = IPgosSDKCpp::Get().GetServerBattleAPI();
if (battle)
{
FString BattleSessionID;
TArray<FString> PlayerIDs;
TArray<FString> Tags;

battle->UpdateBattleRecord(BattleSessionID, PlayerIDs, Tags, [](const FPgosResult& Ret) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("UpdateBattleRecord成功"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("UpdateBattleRecord失败: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}
从v0.15.0开始,API UpdateBattleRecord中的player_ids已弃用 :::

从v0.15.0开始,API UpdateBattleRecord中的player_ids已弃用

:::

7.5 查询战斗记录

Remark

仅限PGOS Client SDK

调用GetBattleRecordList批量获取玩家的战斗记录。您将获得每个战斗记录的所有键值对,这些是由游戏服务器通过UpdateBattleData设置的。

  • PlayerID:要查询的玩家ID。
  • Tags:获取特定标签的列表,如果tags为空,则不筛选标签。
  • Keys:获取特定键的列表,如果keys为空,则不筛选键。
  • Offset:列表的起始位置。
  • Count:列表的数量。

Battle模块调用GetBattleRecordList如下:

#include "PgosSDKCpp.h"

void SomeUObjectClass::GetBattleRecordList()
{
auto battle = IPgosSDKCpp::Get().GetClientBattleAPI();
if (battle)
{
FString PlayerID;
TArray<FString> Tags;
TArray<FString> Keys;
int32 Offset;
int32 Count;

battle->GetBattleRecordList(PlayerID, Tags, Keys, Offset, Count, [](const FPgosResult& Ret, const FClientBattleListInfo* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("GetBattleRecordList成功"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("GetBattleRecordList失败: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

7.6 查询战斗数据

Remark

仅限PGOS Client SDK

调用GetBattleData批量获取特定Battle Session的战斗数据。您将获得每个战斗数据的指定键值对,这些是由游戏服务器通过UpdateBattleData设置的。

  • BattleSessionID:要查询的ID。
  • Keys:获取特定键的列表,如果keys为空,则不筛选键。

Battle模块调用GetBattleData如下:

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

void SomeUObjectClass::GetBattleData()
{
auto battle = IPgosSDKCpp::Get().GetClientBattleAPI();
if (battle)
{
FString BattleSessionID;
TArray<FString> Keys;

battle->GetBattleData(BattleSessionID, Keys, [](const FPgosResult& Ret, const FClientBattleDataInfo* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("GetBattleData成功"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("GetBattleData成功: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

8. 玩家延迟主题

在为Battle Session分配DS实例时,PGOS通常需要玩家与每个数据中心之间的网络延迟数据,以识别并分配延迟最低的数据中心。为了便于此过程,客户端上的PgosSDK包含内置的速度测试逻辑,在需要时(例如:在 JoinWorld, StartMatchmakingLobby StartBattle 期间)自动收集并填充玩家的延迟数据。工作流程如下:

graph TD A[成功登录PGOS] --> B[获取Ping服务器列表: 10ms – 500ms] B --> C[使用客户端和数据中心的经纬度估算玩家延迟: 瞬时] C --> D[快速测速: 向每个数据中心的Ping服务器发送3个数据包: 约2秒] D --> E[常规测速: 向每个数据中心的Ping服务器发送10个数据包: 约4秒] E --> F[等待30分钟] F --> E

在测速未完成或失败的情况下,将使用估算的玩家延迟。

8.1 获取玩家延迟

玩家延迟数据通常会在适当的时间自动上传到PGOS后端,因此开发人员无需关心。但是,如果需要,开发人员仍然可以通过PgosSDK的Battle模块获取此数据。

接口原型:

/**
* 获取客户端到DS托管数据中心的延迟。
* 一般来说,第一轮延迟测试将在登录PGOS后约2~3秒内完成。
* 如果在测试完成前调用此API,或发生异常,API返回的延迟数据将为空。
*
* @return 客户端到DS托管数据中心的延迟。
*/
FPgosClientDSHostingLatencies GetDSHostingLatencies() const;

/** 客户端到DS托管数据中心的延迟。 */
USTRUCT(BlueprintType)
struct PGOSSDKCPP_API FPgosClientDSHostingLatencies
{
GENERATED_USTRUCT_BODY()
/**
* 客户端到DS托管数据中心的延迟。
* Key: 数据中心名称,其可能值可以在'DataCenterName'定义中查看。
* Value: 到数据中心的延迟,以毫秒为单位。
*/
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "PGOS|Client|Models")
TMap<FString, int32> latencies;
};

示例代码:

#include "PgosSDKCpp.h"

void SomeUObjectClass::SomeFunction()
{
auto Battle = IPgosSDKCpp::Get().GetClientBattleAPI();
if (Battle)
{

Battle->GetDSHostingLatencies([]() {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("GetDSHostingLatencies 成功"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("GetDSHostingLatencies 失败: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

8.2 OnLatencyTestRoundCompleted 事件

为了在每轮玩家延迟测速完成后接收结果,开发人员可以注册OnLatencyTestRoundCompleted事件的回调,该事件在每轮延迟测速完成后触发。

示例代码:

#include "PgosSDKCpp.h"

void SomeUObjectClass::MonitorLatencyTestRoundCompletedEvent()
{
auto Battle = IPgosSDKCpp::Get().GetClientBattleAPI();
if (Battle)
{
Battle->OnLatencyTestRoundCompleted().AddUObject(this, &SomeUObjectClass::OnLatencyTestRoundCompleted);
}
}

void SomeUObjectClass::OnLatencyTestRoundCompleted(const FPgosClientLatencyTestRoundCompletedEvt& Event)
{
UE_LOG(LogTemp, Log, TEXT("OnLatencyTestRoundCompleted"));
}

数据结构:

/** 延迟测试轮次完成时的事件详情。] */
USTRUCT(BlueprintType)
struct PGOSSDKCPP_API FPgosClientLatencyTestRoundCompletedEvt
{
GENERATED_USTRUCT_BODY()
/** 测试轮次的索引,指定这是第几轮(从0开始)。 */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "PGOS|Client|Models")
int32 round_idx = 0;
/** 客户端到DS托管数据中心的延迟。 */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "PGOS|Client|Models")
FPgosClientDSHostingLatencies data;
};

/** 客户端到DS托管数据中心的延迟。 */
USTRUCT(BlueprintType)
struct PGOSSDKCPP_API FPgosClientDSHostingLatencies
{
GENERATED_USTRUCT_BODY()
/**
* 客户端到DS托管数据中心的延迟。
* Key: 数据中心名称,其可能值可以在'DataCenterName'定义中查看。
* Value: 到数据中心的延迟,以毫秒为单位。
*/
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "PGOS|Client|Models")
TMap<FString, int32> latencies;
};

9. 关键错误处理

错误代码相关API处理建议
kBackendNeedRetryActivateBattleSession
HeartbeatBattleSession
TerminateBattleSession
AcceptPlayerBattleSession
RemovePlayerBattleSession
DisconnectPlayerBattleSession
表示对battlesession的操作失败。并发安全由分布式锁机制保证。建议有限次重试直到成功。