查找大厅

游戏通常非常有用,让玩家找到满足特定条件集的大厅,如地图和其他游戏内质量的难度级别。 此搜索功能使玩家能够找到他们想要与所需人员一起使用的游戏会话。

本文介绍如何使用 FindLobbies 使玩家能够查找大厅。 若要了解如何查找游戏中可以使用的大厅,请参阅 常见方案

注意

不建议使用 FindLobbies 执行后台匹配。 强烈建议在该方案中使用 匹配功能。 否则,你必须通过筛选、排序和其他技术(例如在搜索数据字段中使用随机化值)来处理所有尝试加入相同大厅的玩家的冲突。

了解大厅搜索属性和查找大厅之间的关系

玩家通过定义搜索属性使其大厅可发现。 玩家通过使用查询字符串调用 FindLobbies 来查找这些可发现的大厅,以便根据当前活动大厅中定义的搜索属性对搜索结果进行筛选和排序。 与这些查询匹配的大厅将返回给呼叫播放器。

有关定义搜索属性的详细信息,请签出 “创建可搜索的大厅”

如何使用 FindLobbies

调用 FindLobbies时,可以使用筛选器参数将查询限制为仅根据大厅的自定义搜索属性返回与某些条件集匹配的搜索结果。

此外,可以使用排序参数根据搜索属性对从服务返回的结果进行排序。 这非常有用,因为服务只返回有限数量的搜索结果。 排序可确保看到最相关的搜索结果。

常见方案

以下是在游戏中使用 FindLobbies 功能的几种常见方法。

  • 查找游戏中特定游戏模式的游戏会话的大厅
  • 查找好友正在托管的游戏会话的大厅
  • 查找与所有本地玩家具有足够玩家的游戏会话的大厅
  • 在意外的游戏客户端或游戏服务器崩溃后查找你已在其中恢复连接的大厅。

支持的搜索密钥

定义自定义搜索属性时,只允许使用一组受限的密钥。

  • 对于字符串属性,支持以下键:string_key1、string_key2、[...] string_key30
  • 对于数值属性,支持以下键:number_key1、number_key2、[...] number_key30

为 FindLobbies 构造查询字符串

FindLobbies API 的查询字符串以类似于 OData 的语法进行结构化。 筛选器字符串最大为 600 个字符。

这些 OData 运算符可用于撰写查询字符串。 运算符区分大小写。

运算符 含义 示例
eq 等于 string_key1 eq “CaptureTheFlag”
lt 小于 number_key2 lt 10
le 小于等于 number_key2 le 10
gt 大于 number_key3 gt 100
ge 大于等于 number_key3 ge 100
ne ne string_key1 ne 'CaptureTheFlag'
and and string_key1 eq 'CaptureTheFlag' and number_key2 lt 10

注意

比较字符串属性时,请务必将比较值括在单引号中。 例如,“string_key1 eq 'SOME STRING VALUE'”。 无需包装数值属性。

还有预定义的运算符可供使用。 指定时,它们必须以“lobby/”作为前缀。

运算符 含义 示例
memberCount 大厅中的玩家数 lobby/memberCount eq 5
maxMemberCount 大厅中允许的最大玩家数。 lobby/maxMemberCount gt 10
memberCountRemaining 可加入大厅的剩余玩家数 lobby/memberCountRemaining gt 0
membershipLock 大厅的锁定状态,必须为“已解锁”或“已锁定” lobby/membershipLock eq 'Unlocked'
amOwner 所有者的大厅,需要为“true” lobby/amOwner eq 'true'
amMember 你所属的大厅,必须为“true” lobby/amMember eq 'true'
amServer 服务器已加入客户端拥有的大厅,需要为“true” lobby/amServer eq 'true'

此处记录了这些常量的 SDK 定义。

排序

OData 样式字符串,包含此查询的升序(“asc”)或降序(“desc”)。 OrderBy 子句可用于任何搜索编号键或数值的预定义搜索键。 若要按最接近数字的距离进行排序,可以使用名字对象距离按与给定数字搜索键的距离进行排序。 不能对距离排序使用升序或降序。 此字段仅支持一个排序子句或一个距离子句。 如果未提供排序,或者给定排序需要中断,则默认排序将根据创建时间降序。

示例 含义
number_key1 asc 按数字搜索键升序排序
lobby/memberCount desc 按数字搜索键降序排序
distance(number_key1 = 5) 根据与给定数字的距离排序
默认 按创建时间降序排序

使用大厅和匹配 SDK 查找大厅的示例

在此示例中,玩家希望查找具有以下要求的所有大厅:

  • 游戏模式为“DeathMatch”
  • 竞赛样式为“排名”
  • 玩家的技能水平在大厅的最小和最大技能限制范围内。

此外,玩家希望结果按照以下准则进行排序:

  • 具有最接近玩家技能水平的最佳技能级别的大厅应排序得更高
static PFMultiplayerHandle g_pfmHandle = nullptr;

#define SUPPORT_XBL_CROSSPLAY

#define PFLOBBY_SEARCH_KEY_GAME_MODE "string_key1"
#define PFLOBBY_SEARCH_KEY_COMPETITION_STYLE "string_key2"
#define PFLOBBY_SEARCH_KEY_SKILL "number_key1"

#define GAME_MODE_DEATH_MATCH "DeathMatch"
#define COMPETITION_STYLE_RANKED "Ranked"

// Find lobbies based on player's search criteria.
void FindGamesWithRuntimeQuery(
    uint32_t minimumSkill,
    uint32_t maximumSkill,
    uint32_t optimalSkill)
{
    PFLobbySearchFriendsFilter friendsFilter;
    friendsFilter.includeSteamFriends = true;
#ifdef SUPPORT_XBL_CROSSPLAY
    friendsFilter.includeXboxFriendsToken = MyGame::GetLocalUserXboxToken();
#endif // SUPPORT_XBL_CROSSPLAY

    // Limit results based on friend's filter.
    PFLobbySearchConfiguration searchConfiguration = { 0 };
    searchConfiguration.friendsFilter = &friendsFilter;

    // Create filter string based on player's search parameters.
    std::string filterString;
    filterString += PFLOBBY_SEARCH_KEY_GAME_MODE + std::string(" eq ") + "'" + GAME_MODE_DEATH_MATCH + "'";
    filterString += " and ";
    filterString += PFLOBBY_SEARCH_KEY_COMPETITION_STYLE + std::string(" eq ") + "'" + COMPETITION_STYLE_RANKED + "'";
    filterString += " and ";
    filterString += PFLOBBY_SEARCH_KEY_SKILL + std::string(" -ge ") + std::to_string(minimumSkill);
    filterString += " and ";
    filterString += PFLOBBY_SEARCH_KEY_SKILL + std::string(" -le ") + std::to_string(maximumSkill);

    // Create sort string based on player's sort preference.
    std::string sortString;
    sortString += std::string("distance{") + PFLOBBY_SEARCH_KEY_SKILL + "=" + std::to_string(optimalSkill) + "}";

    searchConfiguration.filterString = filterString.c_str();
    searchConfiguration.sortString = sortString.c_str();

    HRESULT hr = PFMultiplayerFindLobbies(g_pfmHandle, &m_localUser, &searchConfiguration, nullptr);
}

void ProcessStateChanges()
{
    while (true)
    {
        uint32_t stateChangeCount;
        const PFLobbyStateChange* const* stateChanges;
        RETURN_VOID_IF_FAILED(PFMultiplayerStartProcessingLobbyStateChanges(
            g_pfmHandle,
            &stateChangeCount,
            &stateChanges));

        for (uint32_t i = 0; i < stateChangeCount; ++i)
        {
            const PFLobbyStateChange* stateChange = stateChanges[i];
            switch (stateChange->stateChangeType)
            {
                case PFLobbyStateChangeType::FindLobbiesCompleted:
                {
                    GuiPostCurrentLobbySearchResults(
                        static_cast<const PFLobbyFindLobbiesCompletedStateChange&>(*stateChange));
                    break;
                }
                default:
                {
                    break;
                }
            }
        }

        RETURN_VOID_IF_FAILED(PFMultiplayerFinishProcessingLobbyStateChanges(
            g_pfmHandle,
            stateChangeCount,
            stateChanges));
    }
}

// Update game UI to display search results when a list of matching lobbies is returned.
void GuiPostCurrentLobbySearchResults(
    const PFLobbyFindLobbiesCompletedStateChange& stateChange)
{
    if (FAILED(stateChange.result))
    {
        GuiToastErrorAndExitScreen();
        return;
    }

    for (uint32_t i = 0; i < stateChange.searchResultCount; ++i)
    {
        const PFLobbySearchResult& searchResult = stateChange.searchResults[i];
        GuiPostLobbySearchResultRow(searchResult); // defined elsewhere
    }
}

另请参阅