> ## Documentation Index
> Fetch the complete documentation index at: https://docs.xbox.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 使用 Game Chat 2 C++ API

> XBOX GDK 上 Game Chat 2 C++ API 的逐步解說，涵蓋初始化、新增使用者、設定聊天頻道和錯誤處理模式。

本主題簡要逐步解說如何使用 Game Chat 2 的 C++ API 將語音和文字通訊新增至您的遊戲。

## 必要條件

Game Chat 2 要求您的專案已針對 GDK 進行設定。如需如何進行設定的詳細資料，請參閱[開始使用 Microsoft Game Development Kit](https://learn.microsoft.com/gaming/gdk/docs/services/gdk-dev/get-started/get-started-home)。

編譯 Game Chat 2 需要包含主要的 *GameChat2.h* 標頭。
若要正確連結，您的專案也必須在至少一個編譯單位中包含 *GameChat2Impl.h* (建議使用通用的先行編譯標頭，因為這些虛設函式實作很小，編譯器很容易將其產生為「內嵌」)。

Game Chat 2 介面不需要專案在使用 C++/CX 或傳統 C++ 編譯之間做選擇。兩者皆可使用。此實作也不會擲回例外狀況作為非嚴重錯誤回報的方式。如果您願意，可以輕鬆地從無例外狀況的專案使用它。不過，此實作確實會擲回例外狀況作為嚴重錯誤回報的方式。(如需詳細資料，請參閱本主題稍後的[失敗模型](https://docs.xbox.com/zh-TW/services/xbox-services/multiplayer/chat/game-chat2/using-game-chat-2.md#FAILURE_MODEL)一節。)

## 初始化

首先以適用於單一執行個體初始化存留期的參數初始化 Game Chat 2 單一執行個體，開始與程式庫互動。單一執行個體是透過呼叫 [chat\_manager::initialize](https://docs.xbox.com/zh-TW/reference/chat/gamechat2/classes/chat_manager/methods/chat_manager_initialize.md) 來初始化的，如下所示。

```cpp theme={null}
chat_manager::singleton_instance().initialize(...);
```

> [!NOTE]
>
> 您必須透過 `RegisterAppStateChangeNotification` 註冊暫停和繼續事件。暫停時，您必須使用 [chat\_manager::cleanup()](https://docs.xbox.com/zh-TW/reference/chat/gamechat2/classes/chat_manager/methods/chat_manager_cleanup.md) 清除 Game Chat 2。繼續時，您應該重新初始化 Game Chat 2。如果您嘗試在暫停/繼續週期中使用它，它可能會當機。

## 設定使用者

### 將使用者新增至您的 Microsoft Game Development Kit (GDK) 遊戲

將使用者新增至 Game Chat 2 執行個體之前，請確保他們已新增至 GDK 遊戲。
這是使用 [XUserAddAsync API](https://docs.xbox.com/zh-TW/reference/system/xuser/functions/xuseraddasync.md) 完成的。如需使用此 API 的詳細資訊，請參閱[使用者身分識別和 XUser](https://docs.xbox.com/zh-TW/build/core-features/common/user/player-identity-xuser.md)。

取得您想要新增至 Game Chat 2 之使用者的 `XUserHandle` 之後，您需要使用 [XUserGetId API](https://docs.xbox.com/zh-TW/reference/system/xuser/functions/xusergetid.md) 取得使用者的 XBOX 使用者識別碼 (XUID)。
使用者必須在線上，且您必須取得使用者對此步驟的同意。

[XUserGetId](https://docs.xbox.com/zh-TW/reference/system/xuser/functions/xusergetid.md) 會以 `uint64_t` 提供 XUID。您必須將 XUID 轉換為 `std::wstring`，才能搭配 Game Chat 2 使用。

以下是程式碼範例，示範在您擁有 `XUserHandle` 之後如何將使用者新增至 Game Chat 2。
\[!NOTE] 請注意，呼叫 [XUserResolveIssueWithUiAsync](https://docs.xbox.com/zh-TW/reference/system/xuser/functions/xuserresolveissuewithuiasync.md) 會顯示系統對話方塊。

```cpp theme={null}
HRESULT
AddChatUserFromXUserHandle(
    _In_ XUserHandle user,
    _In_ XTaskQueueHandle queueHandle,
    _Outptr_result_maybenull_ Xs::game_chat_2::chat_user** chatUser
    )
{
    *chatUser = nullptr;
    uint64_t xuid;
    HRESULT hr = XUserGetId(user, &xuid);
    if (hr == E_GAMEUSER_RESOLVE_USER_ISSUE_REQUIRED)
    {
        XAsyncBlock* asyncBlock = new (std::nothrow) XAsyncBlock;
        if (asyncBlock != nullptr)
        {
            ZeroMemory(asyncBlock, sizeof(*asyncBlock));
            asyncBlock->queue = queueHandle;
            hr = XUserResolveIssueWithUiAsync(user, nullptr, asyncBlock);
            if (SUCCEEDED(hr))
            {
                hr = XAsyncGetStatus(asyncBlock, true);
                if (SUCCEEDED(hr))
                {
                    hr = XUserGetId(user, &xuid);
                }
            }
            delete asyncBlock;
        }
        else
        {
            hr = E_OUTOFMEMORY;
        }
    }

    if (SUCCEEDED(hr))
    {
        try
        {
            std::wstring xuidString = std::to_wstring(xuid);

            // If the user has already been added, this will return the existing user.
            *chatUser = Xs::game_chat_2::chat_manager::singleton_instance().add_local_user(xuidString.c_str());
        }
        catch (const std::bad_alloc&)
        {
            hr = E_OUTOFMEMORY;
        }
    }

    return hr;
}
```

### 將使用者新增至 Game Chat 2

執行個體初始化之後，您必須使用 [chat\_manager::add\_local\_user](https://docs.xbox.com/zh-TW/reference/chat/gamechat2/classes/chat_manager/methods/chat_manager_add_local_user.md) 將本機使用者新增至 Game Chat 2 執行個體。在此範例中，使用者 A 代表本機使用者。

```cpp theme={null}
chat_user* chatUserA = chat_manager::singleton_instance().add_local_user(<user_a_xuid>);
```

接下來，新增遠端使用者，以及用來代表使用者所在之遠端「端點」的識別碼。
*端點*是在遠端裝置上執行的應用程式執行個體。

在此範例中，使用者 B 位於端點 X。使用者 C 和 D 位於端點 Y。
端點 X 被任意指派識別碼 "1"。端點 Y 被任意指派識別碼 "2"。

透過下列呼叫告知 Game Chat 2 有哪些遠端使用者。

```cpp theme={null}
chat_user* chatUserB = chat_manager::singleton_instance().add_remote_user(<user_b_xuid>, 1);
chat_user* chatUserC = chat_manager::singleton_instance().add_remote_user(<user_c_xuid>, 2);
chat_user* chatUserD = chat_manager::singleton_instance().add_remote_user(<user_d_xuid>, 2);
```

接下來，設定每位遠端使用者與本機使用者之間的通訊關係。
在此範例中，假設使用者 A 和使用者 B 在同一隊。允許雙向通訊。
`c_communicationRelationshipSendAndReceiveAll` 是在 *GameChat2.h* 中定義的常數，代表雙向通訊。

使用 [chat\_user\_local::set\_communication\_relationship](https://docs.xbox.com/zh-TW/reference/chat/gamechat2/classes/chat_user/chat_user_local/methods/chat_user_local_set_communication_relationship.md) 設定使用者 A 與使用者 B 的關係。

```cpp theme={null}
chatUserA->local()->set_communication_relationship(chatUserB, c_communicationRelationshipSendAndReceiveAll);
```

假設使用者 C 和 D 是「觀眾」，應該允許他們聆聽使用者 A，但不能說話。
`c_communicationRelationshipSendAll` 是在 *GameChat2.h* 中定義的常數，代表此單向通訊。

依下列方式設定關係。

```cpp theme={null}
chatUserA->local()->set_communication_relationship(chatUserC, c_communicationRelationshipSendAll);
chatUserA->local()->set_communication_relationship(chatUserD, c_communicationRelationshipSendAll);
```

如需所有四位本機使用者之關係設定的範例，請參閱本主題稍後的[案例](https://docs.xbox.com/zh-TW/services/xbox-services/multiplayer/chat/game-chat2/using-game-chat-2.md#scenarios)一節。

如果在任何時候，有遠端使用者已新增至單一執行個體，但尚未設定為與任何本機使用者通訊，這是沒問題的。
在使用者決定隊伍或可以任意變更發言頻道的案例中，這是可以預期的。

Game Chat 2 只會為已新增至執行個體的使用者快取資訊 (例如隱私權關係和信譽)，因此告知 Game Chat 2 所有可能的使用者是很有用的，即使他們在特定時間點無法與任何本機使用者交談也一樣。

最後，假設使用者 D 已離開遊戲，應從本機 Game Chat 2 執行個體中移除。
這可以使用 [chat\_manager::remove\_user](https://docs.xbox.com/zh-TW/reference/chat/gamechat2/classes/chat_manager/methods/chat_manager_remove_user.md) 完成，如下所示。

```cpp theme={null}
chat_manager::singleton_instance().remove_user(chatUserD);
```

呼叫 `chat_manager::remove_user()` 可能會使使用者物件失效。如果您使用的是[即時音訊操作](https://docs.xbox.com/zh-TW/services/xbox-services/multiplayer/chat/game-chat2/real-time-audio-manipulation.md)，請參閱[聊天使用者存留期](https://docs.xbox.com/zh-TW/services/xbox-services/multiplayer/chat/game-chat2/real-time-audio-manipulation.md#chat-user-lifetimes)以取得詳細資訊。否則，呼叫 `chat_manager::remove_user()` 時，使用者物件會立即失效。關於何時可以移除使用者的細微限制，詳述於本主題稍後的[處理狀態變更](#processing-state-changes)一節。

## 處理資料畫面

Game Chat 2 沒有自己的傳輸層。傳輸層必須由應用程式提供。
此外掛程式是由應用程式定期、頻繁地呼叫 [chat\_manager::start\_processing\_data\_frames()](https://docs.xbox.com/zh-TW/reference/chat/gamechat2/classes/chat_manager/methods/chat_manager_start_processing_data_frames.md) 和 [chat\_manager::finish\_processing\_data\_frames()](https://docs.xbox.com/zh-TW/reference/chat/gamechat2/classes/chat_manager/methods/chat_manager_finish_processing_data_frames.md) 這對方法來管理的。這些方法是 Game Chat 2 向應用程式提供傳出資料的方式。

這些方法的設計是為了快速運作。它們可以在專用的網路執行緒上頻繁輪詢。
這提供一個方便的位置來擷取所有已排入佇列的資料，而不必擔心網路時間的無法預測性或多執行緒回呼的複雜性。

呼叫 `chat_manager::start_processing_data_frames()` 時，所有已排入佇列的資料都會以 [game\_chat\_data\_frame](https://docs.xbox.com/zh-TW/reference/chat/gamechat2/structs/game_chat_data_frame.md) 結構指標陣列的形式回報。
應用程式應逐一查看陣列、檢查目標端點，並使用應用程式的網路層將資料傳遞至適當的遠端應用程式執行個體。

處理完陣列中所有的 [game\_chat\_data\_frame](https://docs.xbox.com/zh-TW/reference/chat/gamechat2/structs/game_chat_data_frame.md) 結構之後，應呼叫 `chat_manager:finish_processing_data_frames()` 將陣列傳回 Game Chat 2 以釋放資源。
如下列範例所示。

```cpp theme={null}
uint32_t dataFrameCount;
game_chat_data_frame_array dataFrames;
chat_manager::singleton_instance().start_processing_data_frames(&dataFrameCount, &dataFrames);
for (uint32_t dataFrameIndex = 0; dataFrameIndex < dataFrameCount; ++dataFrameIndex)
{
    game_chat_data_frame const* dataFrame = dataFrames[dataFrameIndex];

    // Title-written function responsible for sending packet to remote instances of GameChat 2.
    HandleOutgoingDataFrame(
        dataFrame->packet_byte_count,
        dataFrame->packet_buffer,
        dataFrame->target_endpoint_identifier_count,
        dataFrame->target_endpoint_identifiers,
        dataFrame->transport_requirement
        );
}
chat_manager::singleton_instance().finish_processing_data_frames(dataFrames);
```

資料畫面處理得越頻繁，使用者感受到的音訊延遲就越低。
音訊會合併成 40 毫秒的資料畫面。這是建議的輪詢週期。

## 處理狀態變更

Game Chat 2 會透過應用程式定期、頻繁地呼叫 [chat\_manager::start\_processing\_state\_changes()](https://docs.xbox.com/zh-TW/reference/chat/gamechat2/classes/chat_manager/methods/chat_manager_start_processing_state_changes.md) 和 [chat\_manager::finish\_processing\_state\_changes()](https://docs.xbox.com/zh-TW/reference/chat/gamechat2/classes/chat_manager/methods/chat_manager_finish_processing_state_changes.md) 這對方法，向應用程式提供更新，例如收到的文字訊息。
這些方法運作快速，因此可以在 UI 轉譯迴圈中的每個圖形畫面呼叫。
這提供一個方便的位置來擷取所有已排入佇列的變更，而不必擔心網路時間的無法預測性或多執行緒回呼的複雜性。

呼叫 `chat_manager::start_processing_state_changes()` 時，所有已排入佇列的更新都會以 [game\_chat\_state\_change](https://docs.xbox.com/zh-TW/reference/chat/gamechat2/structs/game_chat_state_change.md) 結構指標陣列的形式回報。
應用程式應逐一查看陣列、檢查基底結構以了解其更具體的類型、將基底結構轉換為對應的更詳細類型，然後適當地處理該更新。

處理完陣列中目前可用的所有 [game\_chat\_state\_change](https://docs.xbox.com/zh-TW/reference/chat/gamechat2/structs/game_chat_state_change.md) 物件之後，應呼叫 `chat_manager::finish_processing_state_changes()` 將陣列傳回 Game Chat 2 以釋放資源。
如下列範例所示。

```cpp theme={null}
uint32_t stateChangeCount;
game_chat_state_change_array gameChatStateChanges;
chat_manager::singleton_instance().start_processing_state_changes(&stateChangeCount, &gameChatStateChanges);

std::list<Xs::game_chat_2::chat_user*> usersWithPrivilegeIssues;
std::list<Xs::game_chat_2::chat_user*> usersWithPrivilegeCheckIssues;
for (uint32_t stateChangeIndex = 0; stateChangeIndex < stateChangeCount; ++stateChangeIndex)
{
    switch (gameChatStateChanges[stateChangeIndex]->state_change_type)
    {
        case game_chat_state_change_type::text_chat_received:
        {
            HandleTextChatReceived(static_cast<const game_chat_text_chat_received_state_change*>(gameChatStateChanges[stateChangeIndex]));
            break;
        }

        case Xs::game_chat_2::game_chat_state_change_type::transcribed_chat_received:
        {
            HandleTranscribedChatReceived(static_cast<const Xs::game_chat_2::game_chat_transcribed_chat_received_state_change*>(gameChatStateChanges[stateChangeIndex]));
            break;
        }
        case Xs::game_chat_2::game_chat_state_change_type::communication_relationship_adjuster_changed:
        {
            HandleAdjusterChangedStateReceived(static_cast<const Xs::game_chat_2::game_chat_communication_relationship_adjuster_changed_state_change*>(gameChatStateChanges[stateChangeIndex]), usersWithPrivilegeIssues, usersWithPrivilegeCheckIssues);
            break;
        }

        ...
    }
}
chat_manager::singleton_instance().finish_processing_state_changes(gameChatStateChanges);
```

由於 `chat_manager::remove_user()` 會立即使與使用者物件相關聯的記憶體失效，而狀態變更可能包含使用者物件的指標，因此處理狀態變更時不得呼叫 `chat_manager::remove_user()`。

## 文字聊天

若要傳送文字聊天，請使用 [chat\_user::chat\_user\_local::send\_chat\_text()](https://docs.xbox.com/zh-TW/reference/chat/gamechat2/classes/chat_user/chat_user_local/methods/chat_user_local_send_chat_text.md)。
如下列範例所示。

```cpp theme={null}
chatUserA->local()->send_chat_text(L"Hello");
```

Game Chat 2 會產生包含此訊息的資料畫面。資料畫面的目標端點是與已設定為接收來自本機使用者之文字的使用者相關聯的端點。
當遠端端點處理資料時，訊息會透過 [game\_chat\_text\_chat\_received\_state\_change](https://docs.xbox.com/zh-TW/reference/chat/gamechat2/structs/game_chat_text_chat_received_state_change.md) 公開。

就像語音聊天一樣，文字聊天也會遵守權限和隱私權限制。
如果一對使用者已設定為允許文字聊天，但權限或隱私權限制不允許該通訊，文字訊息就會被捨棄。

## 協助工具

協助工具需要支援文字聊天輸入和顯示。

需要文字輸入，是因為即使在過去未廣泛使用實體鍵盤的平台或遊戲類型上，使用者也可以將系統設定為使用文字轉語音輔助技術。

同樣地，需要文字顯示，是因為使用者可以將系統設定為使用語音轉文字。

您可以分別呼叫 [chat\_user::chat\_user\_local::text\_to\_speech\_conversion\_preference\_enabled()](https://docs.xbox.com/zh-TW/reference/chat/gamechat2/classes/chat_user/chat_user_local/methods/chat_user_local_text_to_speech_conversion_preference_enabled.md) 和 [chat\_user::chat\_user\_local::speech\_to\_text\_conversion\_preference\_enabled()](https://docs.xbox.com/zh-TW/reference/chat/gamechat2/classes/chat_user/chat_user_local/methods/chat_user_local_speech_to_text_conversion_preference_enabled.md) 方法，偵測本機使用者的這些偏好設定。建議您根據使用者偏好設定有條件地啟用文字。

### 文字轉語音

當使用者啟用文字轉語音時，[chat\_user::chat\_user\_local::text\_to\_speech\_conversion\_preference\_enabled()](https://docs.xbox.com/zh-TW/reference/chat/gamechat2/classes/chat_user/chat_user_local/methods/chat_user_local_text_to_speech_conversion_preference_enabled.md) 會傳回 `true`。偵測到此狀態時，應用程式必須提供文字輸入方法。

取得由實體或虛擬鍵盤提供的文字輸入之後，請將字串傳遞至 [chat\_user::chat\_user\_local::synthesize\_text\_to\_speech()](https://docs.xbox.com/zh-TW/reference/chat/gamechat2/classes/chat_user/chat_user_local/methods/chat_user_local_synthesize_text_to_speech.md) 方法。Game Chat 2 會根據字串和使用者的協助工具語音偏好設定，偵測並合成音訊資料。
如下列範例所示。

```cpp theme={null}
chat_userA->local()->synthesize_text_to_speech(L"Hello");
```

作為此作業的一部分所合成的音訊，會傳輸至已設定為接收來自此本機使用者之音訊的所有使用者。
如果對未啟用文字轉語音的使用者呼叫 `chat_user::chat_user_local::synthesize_text_to_speech()`，Game Chat 2 不會採取任何動作。

### 語音轉文字

當使用者啟用語音轉文字時，[chat\_user::chat\_user\_local::speech\_to\_text\_conversion\_preference\_enabled()](https://docs.xbox.com/zh-TW/reference/chat/gamechat2/classes/chat_user/chat_user_local/methods/chat_user_local_speech_to_text_conversion_preference_enabled.md) 會傳回 `true`。偵測到此狀態時，應用程式必須準備好提供與轉譯聊天訊息相關聯的 UI。Game Chat 2 會自動轉譯每位遠端使用者的音訊，並透過 [game\_chat\_transcribed\_chat\_received\_state\_change](https://docs.xbox.com/zh-TW/reference/chat/gamechat2/structs/game_chat_transcribed_chat_received_state_change.md) 結構將其公開。

### 語音轉文字的效能考量

啟用語音轉文字時，每部遠端裝置上的 Game Chat 2 執行個體都會與語音服務端點起始 WebSocket 連線。
每個遠端 Game Chat 2 用戶端都會透過此 WebSocket 將音訊上傳至語音服務端點。語音服務端點偶爾會將轉譯訊息傳回遠端裝置。
遠端裝置接著會將轉譯訊息 (也就是文字訊息) 傳送至本機裝置。轉譯的訊息會由 Game Chat 2 提供給應用程式進行轉譯。

因此，語音轉文字的主要效能成本是網路使用量。
大部分的網路流量是上傳已編碼的音訊。
WebSocket 會上傳已由 Game Chat 2 在「一般」語音聊天路徑中編碼的音訊。應用程式可以透過 [chat\_manager::set\_audio\_encoding\_bitrate](https://docs.xbox.com/zh-TW/reference/chat/gamechat2/classes/chat_manager/methods/chat_manager_set_audio_encoding_bitrate.md) 控制位元速率。

## UI

建議在向使用者顯示 UI 的任何地方，特別是在計分板等玩家代號清單中，也顯示靜音/說話圖示作為使用者的回饋。
這是透過呼叫 [chat\_user::chat\_indicator()](https://docs.xbox.com/zh-TW/reference/chat/gamechat2/classes/chat_user/methods/chat_user_chat_indicator.md) 來擷取代表該使用者目前即時聊天狀態的 [game\_chat\_user\_chat\_indicator](https://docs.xbox.com/zh-TW/reference/chat/gamechat2/enums/game_chat_user_chat_indicator.md) 列舉來完成的。下列範例示範如何擷取 `chatUserA` 變數所指向之 [chat\_user](https://docs.xbox.com/zh-TW/reference/chat/gamechat2/classes/chat_user/chat_user.md) 物件的指標值，以決定要指派給 `iconToShow` 變數的特定圖示常數值。

```cpp theme={null}
switch (chatUserA->chat_indicator())
{
   case game_chat_user_chat_indicator::silent:
   {
       iconToShow = Icon_InactiveSpeaker;
       break;
   }

   case game_chat_user_chat_indicator::talking:
   {
       iconToShow = Icon_ActiveSpeaker;
       break;
   }

   case game_chat_user_chat_indicator::local_microphone_muted:
   {
       iconToShow = Icon_MutedSpeaker;
       break;
   }
   ...
}
```

[chat\_user::chat\_indicator()](https://docs.xbox.com/zh-TW/reference/chat/gamechat2/classes/chat_user/methods/chat_user_chat_indicator.md) 所回報的值預期會經常變更，例如當玩家開始和停止說話時。
因此，它的設計是支援應用程式在每個 UI 畫面輪詢它。

## 靜音

[chat\_user::chat\_user\_local::set\_microphone\_muted()](https://docs.xbox.com/zh-TW/reference/chat/gamechat2/classes/chat_user/chat_user_local/methods/chat_user_local_set_microphone_muted.md) 方法可用來切換本機使用者麥克風的靜音狀態。當麥克風被設為靜音時，不會擷取來自該麥克風的任何音訊。如果使用者位於共用裝置 (例如 Kinect) 上，靜音狀態會套用至所有使用者。

[chat\_user::chat\_user\_local::microphone\_muted()](https://docs.xbox.com/zh-TW/reference/chat/gamechat2/classes/chat_user/chat_user_local/methods/chat_user_local_microphone_muted.md) 方法可用來擷取本機使用者麥克風的靜音狀態。此方法只會反映本機使用者的麥克風是否已透過呼叫 `chat_user::chat_user_local::set_microphone_muted()` 在軟體中設為靜音。此方法不會反映由硬體控制的靜音，例如透過使用者耳機上的按鈕。

沒有透過 Game Chat 2 擷取使用者音訊裝置之硬體靜音狀態的方法。

[chat\_user::chat\_user\_local::set\_remote\_user\_muted()](https://docs.xbox.com/zh-TW/reference/chat/gamechat2/classes/chat_user/chat_user_local/methods/chat_user_local_set_remote_user_muted.md) 方法可用來切換遠端使用者相對於特定本機使用者的靜音狀態。當遠端使用者被設為靜音時，本機使用者將不會聽到來自遠端使用者的任何音訊，也不會收到任何文字訊息。

## 不良信譽自動靜音

一般而言，遠端使用者一開始是未靜音的。
在下列情況下，Game Chat 2 會以靜音狀態啟動使用者：

1. 遠端使用者與本機使用者不是好友。
2. 遠端使用者具有不良信譽旗標。

當使用者因此作業而被設為靜音時，`chat_user::chat_indicator()` 會傳回 `game_chat_user_chat_indicator::reputation_restricted`。
第一次呼叫包含該遠端使用者作為目標使用者的 `chat_user::chat_user_local::set_remote_user_muted()` 時，就會覆寫此狀態。

## 權限和隱私權

除了遊戲所設定的通訊關係之外，Game Chat 2 也會強制執行權限和隱私權限制。
Game Chat 2 會在第一次新增使用者時執行權限和隱私權限制查閱。在這些作業完成之前，使用者的 `chat_user::chat_indicator()` 一律會傳回 `game_chat_user_chat_indicator::silent`。

如果與使用者的通訊受到權限或隱私權限制的影響，使用者的 `chat_user::chat_indicator()` 會傳回 `game_chat_user_chat_indicator::platform_restricted`。
平台通訊限制同時適用於語音和文字聊天。絕不會發生文字聊天被平台限制封鎖但語音聊天沒有被封鎖的情況，反之亦然。

[chat\_user::chat\_user\_local::get\_effective\_communication\_relationship()](https://docs.xbox.com/zh-TW/reference/chat/gamechat2/classes/chat_user/chat_user_local/methods/chat_user_local_get_effective_communication_relationship.md) 可用來協助區分使用者何時因權限和隱私權作業未完成而無法通訊。
它會以 [game\_chat\_communication\_relationship\_flags](https://docs.xbox.com/zh-TW/reference/chat/gamechat2/enums/game_chat_communication_relationship_flags.md) 的形式傳回 Game Chat 2 所強制執行的通訊關係，以及以 [game\_chat\_communication\_relationship\_adjuster](https://docs.xbox.com/zh-TW/reference/chat/gamechat2/enums/game_chat_communication_relationship_adjuster.md) 列舉形式表示的關係可能不等於所設定之關係的原因。

例如，如果查閱作業仍在進行中，[game\_chat\_communication\_relationship\_adjuster](https://docs.xbox.com/zh-TW/reference/chat/gamechat2/enums/game_chat_communication_relationship_adjuster.md) 會是 `game_chat_communication_relationship_adjuster::initializing`。
不應使用此方法來影響 UI。(如需詳細資訊，請參閱本主題稍早的 [UI](#ui) 一節。)

如果 Game Chat 2 遇到權限問題，會在 [communication\_relationship\_adjuster\_changed](https://docs.xbox.com/zh-TW/reference/chat/gamechat2/structs/game_chat_stream_state_change.md) 狀態變更中回報。

如果 Game Chat 2 因為無法復原的原因而無法擷取使用者的權限，會回報為 `game_chat_communication_relationship_adjuster::privilege_check_failure` 調整器。

如果 Game Chat 2 因為使用者可能可以解決的原因而無法擷取使用者的權限，會回報為 `game_chat_communication_relationship_adjuster::resolve_user_issue` 調整器。

如果使用者缺少可能可以透過 UI 解決的權限，會回報為 `game_chat_communication_relationship_adjuster::privilege` 調整器。

在這些情況下，通訊將會受到限制。

以下範例說明如何檢查使用者是否有下列其中一個常見問題。

1. 使用者需要同意 XBOX 服務，Game Chat 2 才能檢查權限。
2. 使用者的帳戶設定為拒絕權限 (例如，是兒童帳戶，因此無法使用聊天)。

```cpp theme={null}
void
HandleAdjusterChangedStateReceived (
    _In_ const Xs::game_chat_2::game_chat_communication_relationship_adjuster_changed_state_change* adjusterChange,
    _Inout_ std::list<Xs::game_chat_2::chat_user*>& usersWithPrivilegeIssues,
    _Inout_ std::list<Xs::game_chat_2::chat_user*>& usersWithPrivilegeCheckIssues
    )
{
    Xs::game_chat_2::game_chat_communication_relationship_flags communicationRelationship;
    Xs::game_chat_2::game_chat_communication_relationship_adjuster communicationRelationshipAdjuster;
    adjusterChange->local_user->local()->get_effective_communication_relationship(
        adjusterChange->target_user,
        &communicationRelationship,
        &communicationRelationshipAdjuster);

    if (communicationRelationshipAdjuster == Xs::game_chat_2::game_chat_communication_relationship_adjuster::privilege)
    {
        // The local user has privilege issues.
        usersWithPrivilegeIssues.push_back(adjusterChange->local_user);
    }
    else if (communicationRelationshipAdjuster == Xs::game_chat_2::game_chat_communication_relationship_adjuster::resolve_user_issue)
    {
        // The local user has an issue checking privileges.
        usersWithPrivilegeCheckIssues.push_back(adjusterChange->local_user);
    }
}
```

對於以 `game_chat_communication_relationship_adjuster::privilege` 調整器回報的問題，您可以使用 `XUserPrivilegeOptions::None` 和 `XUserPrivilege::Communications` 呼叫 [XUserResolvePrivilegeWithUiAsync](https://docs.xbox.com/zh-TW/reference/system/xuser/functions/xuserresolveprivilegewithuiasync.md)，以嘗試解決問題。

對於以 `game_chat_communication_relationship_adjuster::resolve_user_issue` 調整器回報的問題，您可以使用 `nullptr` 作為 URL 呼叫 [XUserResolveIssueWithUiAsync](https://docs.xbox.com/zh-TW/reference/system/xuser/functions/xuserresolveissuewithuiasync.md)，以嘗試解決問題。

建議您顯示指出有權限問題的 UI。讓使用者透過按下按鈕或選單選項來決定是否要嘗試解決問題。

使用者可能無法或不願意解決問題。
如果使用者確實解決了問題，將在下次使用者新增至 Game Chat 2 時生效。

> [!NOTE]
>
> 處理狀態變更時 (也就是在呼叫 [chat\_manager::start\_processing\_state\_changes()](https://docs.xbox.com/zh-TW/reference/chat/gamechat2/classes/chat_manager/methods/chat_manager_start_processing_state_changes.md) 之後，且在對應呼叫 [chat\_manager::finish\_processing\_state\_changes()](https://docs.xbox.com/zh-TW/reference/chat/gamechat2/classes/chat_manager/methods/chat_manager_finish_processing_state_changes.md) 之前)，不得呼叫 [chat\_manager::remove\_user()](https://docs.xbox.com/zh-TW/reference/chat/gamechat2/classes/chat_manager/methods/chat_manager_remove_user.md)。在處理狀態變更時呼叫 `chat_manager::remove_user()`，可能會使與已移除使用者相關聯的記憶體失效。
> 如果您看到 `game_chat_communication_relationship_adjuster::privilege` 調整器並想要嘗試解決使用者權限，應等到處理狀態變更之後再嘗試。

若要從 XUID 取得呼叫 `XUserResolvePrivilegeWithUiAsync` 所需的 `XUserHandle`，您可以使用 [XUserFindUserById](https://docs.xbox.com/zh-TW/reference/system/xuser/functions/xuserfinduserbyid.md) API 取得新的 `XUserHandle`。或者，您可以保留透過 [XUserAddAsync](https://docs.xbox.com/zh-TW/reference/system/xuser/functions/xuseraddasync.md) 取得的控制代碼，並追蹤哪個 XUID 對應到它。

以下範例說明如何解決這些問題。

```cpp theme={null}
// If we got an Xs::game_chat_2::game_chat_communication_relationship_adjuster::resolve_user_issue,
// we need to try and fix our issue, if we haven't already, and then remove and re-add that user.
for (Xs::game_chat_2::chat_user* localUser : usersWithPrivilegeCheckIssues)
{
    auto asyncBlock = std::make_unique<XAsyncBlock>();
    ZeroMemory(asyncBlock.get(), sizeof(*asyncBlock));
    asyncBlock->queue = g_asyncQueue;

    XUserHandle userHandle;
    hr = XUserFindUserById(localUser->local()->xbox_user_id(), &userHandle);
    if (SUCCEEDED(hr))
    {
        hr = XUserResolveIssueWithUiAsync(
            userHandle,
            nullptr,
            asyncBlock.get());
        if (SUCCEEDED(hr))
        {
            hr = XAsyncGetStatus(asyncBlock.get(), true);
            if (SUCCEEDED(hr))
            {
                // Remove and re-add the user after fixing the privileges.
                // Users must not be removed while processing state changes.
            }
            asyncBlock.release();
        }
    }
}

// If we got an Xs::game_chat_2::game_chat_communication_relationship_adjuster::resolve_user_privilege,
// we need to try and resolve the privileges, if we haven't already, and then remove and re-add that user.
for (Xs::game_chat_2::chat_user* localUser : usersWithPrivilegeIssues)
{
    auto asyncBlock = std::make_unique<XAsyncBlock>();
    ZeroMemory(asyncBlock.get(), sizeof(*asyncBlock));
    asyncBlock->queue = g_asyncQueue;

    XUserHandle userHandle;
    hr = XUserFindUserById(localUser->local()->xbox_user_id(), &userHandle);
    if (SUCCEEDED(hr))
    {
        hr = XUserResolvePrivilegeWithUiAsync(
            userHandle,
            XUserPrivilegeOptions::None,
            XUserPrivilege::Communications,
            asyncBlock.get());
        if (SUCCEEDED(hr))
        {
            hr = XAsyncGetStatus(asyncBlock.get(), true);
            if (SUCCEEDED(hr))
            {
                // Remove and re-add the user after fixing the privileges.
                // Users must not be removed while processing state changes.
            }
            asyncBlock.release();
        }
    }
}
```

## 清除

當應用程式不再需要透過 Game Chat 2 進行通訊時，您應該呼叫 [chat\_manager::cleanup()](https://docs.xbox.com/zh-TW/reference/chat/gamechat2/classes/chat_manager/methods/chat_manager_cleanup.md)。
這可讓 Game Chat 2 回收為管理通訊而配置的資源。

## 失敗模型

Game Chat 2 實作不會擲回例外狀況作為非嚴重錯誤回報的方式。如果您願意，可以輕鬆地從無例外狀況的專案使用它。
不過，Game Chat 2 確實會擲回例外狀況，以通知您嚴重錯誤。

這些錯誤是 API 誤用的結果，例如在初始化執行個體之前將使用者新增至 Game Chat 執行個體，或在使用者物件從 Game Chat 2 執行個體中移除之後存取它。

這些錯誤預期會在開發初期被發現，並可透過修改與 Game Chat 2 互動所使用的模式來更正。
發生此類錯誤時，在引發例外狀況之前，會將錯誤原因的提示列印至偵錯工具。

## 如何設定常見案例

### 按鍵通話

按鍵通話應使用 [chat\_user::chat\_user\_local::set\_microphone\_muted()](https://docs.xbox.com/zh-TW/reference/chat/gamechat2/classes/chat_user/chat_user_local/methods/chat_user_local_set_microphone_muted.md) 實作。
呼叫 `set_microphone_muted(false)` 以允許說話，呼叫 `set_microphone_muted(true)` 以限制說話。
此方法可提供來自 Game Chat 2 的最低延遲回應。

### 隊伍

假設使用者 A 和使用者 B 在藍隊，而使用者 C 和使用者 D 在紅隊。
每位使用者都位於應用程式的唯一執行個體中。

在使用者 A 的裝置上：

```cpp theme={null}
chatUserA->local()->set_communication_relationship(chatUserB, c_communicationRelationshipSendAndReceiveAll);
chatUserA->local()->set_communication_relationship(chatUserC, game_chat_communication_relationship_flags::none);
chatUserA->local()->set_communication_relationship(chatUserD, game_chat_communication_relationship_flags::none);
```

在使用者 B 的裝置上：

```cpp theme={null}
chatUserB->local()->set_communication_relationship(chatUserA, c_communicationRelationshipSendAndReceiveAll);
chatUserB->local()->set_communication_relationship(chatUserC, game_chat_communication_relationship_flags::none);
chatUserB->local()->set_communication_relationship(chatUserD, game_chat_communication_relationship_flags::none);
```

在使用者 C 的裝置上：

```cpp theme={null}
chatUserC->local()->set_communication_relationship(chatUserA, game_chat_communication_relationship_flags::none);
chatUserC->local()->set_communication_relationship(chatUserB, game_chat_communication_relationship_flags::none);
chatUserC->local()->set_communication_relationship(chatUserD, c_communicationRelationshipSendAndReceiveAll);
```

在使用者 D 的裝置上：

```cpp theme={null}
chatUserD->local()->set_communication_relationship(chatUserA, game_chat_communication_relationship_flags::none);
chatUserD->local()->set_communication_relationship(chatUserB, game_chat_communication_relationship_flags::none);
chatUserD->local()->set_communication_relationship(chatUserC, c_communicationRelationshipSendAndReceiveAll);
```

### 廣播

假設使用者 A 是領導者，負責下達命令。使用者 B、C 和 D 只能聆聽。
每位玩家都位於唯一的裝置上。

在使用者 A 的裝置上：

```cpp theme={null}
chatUserA->local()->set_communication_relationship(chatUserB, c_communicationRelationshipSendAll);
chatUserA->local()->set_communication_relationship(chatUserC, c_communicationRelationshipSendAll);
chatUserA->local()->set_communication_relationship(chatUserD, c_communicationRelationshipSendAll);
```

在使用者 B 的裝置上：

```cpp theme={null}
chatUserB->local()->set_communication_relationship(chatUserA, c_communicationRelationshipReceiveAll);
chatUserB->local()->set_communication_relationship(chatUserC, game_chat_communication_relationship_flags::none);
chatUserB->local()->set_communication_relationship(chatUserD, game_chat_communication_relationship_flags::none);
```

在使用者 C 的裝置上：

```cpp theme={null}
chatUserC->local()->set_communication_relationship(chatUserA, c_communicationRelationshipReceiveAll);
chatUserC->local()->set_communication_relationship(chatUserB, game_chat_communication_relationship_flags::none);
chatUserC->local()->set_communication_relationship(chatUserD, game_chat_communication_relationship_flags::none);
```

在使用者 D 的裝置上：

```cpp theme={null}
chatUserD->local()->set_communication_relationship(chatUserA, c_communicationRelationshipReceiveAll);
chatUserD->local()->set_communication_relationship(chatUserB, game_chat_communication_relationship_flags::none);
chatUserD->local()->set_communication_relationship(chatUserC, game_chat_communication_relationship_flags::none);
```

## 參考 API 文件

* [Gamechat2 (API 內容)](https://docs.xbox.com/zh-TW/reference/chat/gamechat2/gamechat2_members.md)
  * 結構
    * [game\_chat\_data\_frame](https://docs.xbox.com/zh-TW/reference/chat/gamechat2/structs/game_chat_data_frame.md)
    * [game\_chat\_state\_change](https://docs.xbox.com/zh-TW/reference/chat/gamechat2/structs/game_chat_state_change.md)
    * [game\_chat\_text\_chat\_received\_state\_change](https://docs.xbox.com/zh-TW/reference/chat/gamechat2/structs/game_chat_text_chat_received_state_change.md)
    * [game\_chat\_transcribed\_chat\_received\_state\_change](https://docs.xbox.com/zh-TW/reference/chat/gamechat2/structs/game_chat_transcribed_chat_received_state_change.md)
    * [communication\_relationship\_adjuster\_changed](https://docs.xbox.com/zh-TW/reference/chat/gamechat2/structs/game_chat_stream_state_change.md)
* [xuser (API 內容)](https://docs.xbox.com/zh-TW/reference/system/xuser/xuser_members.md)
  * 函式
    * [xuseraddasync](https://docs.xbox.com/zh-TW/reference/system/xuser/functions/xuseraddasync.md)
    * [xusergetid](https://docs.xbox.com/zh-TW/reference/system/xuser/functions/xusergetid.md)
    * [XUserResolveIssueWithUiAsync](https://docs.xbox.com/zh-TW/reference/system/xuser/functions/xuserresolveissuewithuiasync.md)
    * [XUserResolvePrivilegeWithUiAsync](https://docs.xbox.com/zh-TW/reference/system/xuser/functions/xuserresolveprivilegewithuiasync.md)
    * [XUserFindUserById](https://docs.xbox.com/zh-TW/reference/system/xuser/functions/xuserfinduserbyid.md)

## 另請參閱

[Game Chat 2 簡介](https://docs.xbox.com/zh-TW/services/xbox-services/multiplayer/chat/game-chat2/game-chat-2-intro.md)

[即時音訊操作](https://docs.xbox.com/zh-TW/services/xbox-services/multiplayer/chat/game-chat2/real-time-audio-manipulation.md)

[API 內容 (GameChat2)](https://docs.xbox.com/zh-TW/reference/chat/gamechat2/gamechat2_members.md)

[Microsoft Game Development Kit](https://docs.xbox.com/zh-TW/services/playfab/sdks/platforms/gdk.md)


## Related topics

- [chat_user](https://docs.xbox.com/zh-TW/reference/chat/gamechat2/classes/chat_user/chat_user.md)
- [Game Chat 2 概觀](https://docs.xbox.com/zh-TW/services/xbox-services/multiplayer/chat/game-chat2/game-chat-2-overview.md)
- [Game Chat 2 簡介](https://docs.xbox.com/zh-TW/services/xbox-services/multiplayer/chat/game-chat2/game-chat-2-intro.md)
- [game_chat_communication_relationship_flags](https://docs.xbox.com/zh-TW/reference/chat/gamechat2/enums/game_chat_communication_relationship_flags.md)
- [send_chat_text](https://docs.xbox.com/zh-TW/reference/chat/gamechat2/classes/chat_user/chat_user_local/methods/chat_user_local_send_chat_text.md)
