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

# 實體控制代碼

> 在 PlayFab C SDK 中管理 PFEntityHandle 認證、參考計數，以及用於已驗證玩家和遊戲呼叫的實體權杖存留期。

**PFEntityHandle** 是您進行 PlayFab 服務呼叫的主要認證。它代表一個已驗證的實體 (玩家 (title\_player\_account) 或遊戲)，並保存 SDK 授權要求所需的實體權杖。每個登入或驗證呼叫都會傳回 **PFEntityHandle**，而每個服務呼叫都需要一個。

## 取得實體控制代碼

您不會直接建立實體控制代碼，而是在成功的登入或驗證呼叫中以輸出的形式取得。

**玩家登入 (Windows 範例)：**

```cpp theme={null}
PFAuthenticationLoginWithXUserRequest request{};
request.createAccount = true;
request.user = userHandle; // XUserHandle from XUserAddAsync

XAsyncBlock async{};
HRESULT hr = PFAuthenticationLoginWithXUserAsync(serviceConfigHandle, &request, &async);
hr = XAsyncGetStatus(&async, true);

PFEntityHandle entityHandle{ nullptr };
size_t bufferSize{};
hr = PFAuthenticationLoginWithXUserGetResultSize(&async, &bufferSize);

std::vector<char> loginResultBuffer(bufferSize);
PFAuthenticationLoginResult const* loginResult{};
hr = PFAuthenticationLoginWithXUserGetResult(
    &async, &entityHandle,
    loginResultBuffer.size(), loginResultBuffer.data(),
    &loginResult, nullptr);
```

**遊戲實體 (伺服器)：**

遊戲實體使用祕密金鑰而非使用者認證進行驗證。傳回的 **PFEntityHandle** 運作方式相同，但它代表的是遊戲而非玩家。如需詳細資訊，請參閱[使用遊戲實體存取 PlayFab](https://devdocs.xbox.com/zh-TW/services/playfab/sdks/c/server.md)。

## 控制代碼擁有權和參考計數

**PFEntityHandle** 會進行參考計數。當您從登入呼叫收到控制代碼時，您擁有一個參考。您可以使用 [**PFEntityDuplicateHandle**](https://devdocs.xbox.com/zh-TW/services/playfab/api-references/c/pfentity/functions/pfentityduplicatehandle.md) 建立其他參考，並使用 [**PFEntityCloseHandle**](https://devdocs.xbox.com/zh-TW/services/playfab/api-references/c/pfentity/functions/pfentityclosehandle.md) 釋放它們。只有在最後一個參考關閉時，基礎實體物件才會被終結。

**規則：**

* 每次呼叫登入 `GetResult` 函式或 **PFEntityDuplicateHandle** 都會給您一個必須關閉的控制代碼。
* 關閉某個控制代碼不會讓同一實體的其他控制代碼失效。
* 在呼叫 [**PFServicesUninitializeAsync**](https://devdocs.xbox.com/zh-TW/services/playfab/api-references/c/pfservices/functions/pfservicesuninitializeasync.md) 之前關閉所有控制代碼。

```cpp theme={null}
// Duplicate a handle for another component
PFEntityHandle secondHandle{ nullptr };
HRESULT hr = PFEntityDuplicateHandle(entityHandle, &secondHandle);

// Both handles are independently valid
// ...

// Each owner closes their own handle
PFEntityCloseHandle(secondHandle);
PFEntityCloseHandle(entityHandle);
```

## 取得實體資訊

### 實體索引鍵

實體索引鍵會識別實體 (其類型和識別碼)。請使用兩次呼叫模式：先取得大小，再取得資料。

```cpp theme={null}
size_t size{};
HRESULT hr = PFEntityGetEntityKeySize(entityHandle, &size);

std::vector<char> buffer(size);
PFEntityKey const* entityKey{};
hr = PFEntityGetEntityKey(entityHandle, buffer.size(), buffer.data(), &entityKey, nullptr);

// entityKey->type is "title_player_account" for players
// entityKey->id is the entity's unique ID
```

### 實體權杖

實體權杖會授權服務呼叫。SDK 會自動管理權杖，但您可以視需要擷取目前的權杖。

```cpp theme={null}
XAsyncBlock async{};
HRESULT hr = PFEntityGetEntityTokenAsync(entityHandle, &async);
hr = XAsyncGetStatus(&async, true);

size_t size{};
hr = PFEntityGetEntityTokenResultSize(&async, &size);

std::vector<char> buffer(size);
const PFEntityToken* entityToken{};
hr = PFEntityGetEntityTokenResult(&async, buffer.size(), buffer.data(), &entityToken, nullptr);

// entityToken->token is the token string
// entityToken->expiration is the optional expiration time (UTC)
```

### 檢查實體類型

使用 [**PFEntityIsTitlePlayer**](https://devdocs.xbox.com/zh-TW/services/playfab/api-references/c/pfentity/functions/pfentityistitleplayer.md) 進行快速檢查，而不需檢查實體索引鍵類型字串：

```cpp theme={null}
bool isTitlePlayer{};
HRESULT hr = PFEntityIsTitlePlayer(entityHandle, &isTitlePlayer);
```

## 玩家實體與遊戲實體

玩家實體和遊戲實體都使用 **PFEntityHandle**，但它們能做的事情有所不同：

| | 玩家實體 | 遊戲實體 |
| - | - | - |
| **取得方式** | 登入呼叫 (例如 `PFAuthenticationLoginWithXUserAsync`) | `PFAuthenticationGetEntityWithSecretKeyAsync` |
| **實體類型** | `title_player_account` | `title` |
| [**PFEntityIsTitlePlayer**](https://devdocs.xbox.com/zh-TW/services/playfab/api-references/c/pfentity/functions/pfentityistitleplayer.md) | 傳回 `true` | 傳回 `false` |
| [**PFEntityGetSecretKey**](https://devdocs.xbox.com/zh-TW/services/playfab/api-references/c/pfentity/functions/pfentitygetsecretkey.md) | 失敗並傳回 `E_PF_NOSECRETKEY` | 傳回祕密金鑰 |
| **可用的 API** | 具有 Client 前置詞和沒有前置詞的 API | 具有 Server 前置詞和沒有前置詞的 API |

沒有前置詞的 API (例如 Inventory、Leaderboards、Data) 通常可由玩家實體和遊戲實體呼叫，除非其文件中另有說明。具有 Client 前置詞的 API 僅限玩家使用，而具有 Server 前置詞的 API 僅限遊戲使用。

**PFEntityGetSecretKey** 會擷取與遊戲實體相關聯的祕密金鑰。對於玩家實體，它會失敗，因為玩家實體沒有祕密金鑰。

```cpp theme={null}
size_t keySize{};
HRESULT hr = PFEntityGetSecretKeySize(entityHandle, &keySize);
if (SUCCEEDED(hr))
{
    std::vector<char> secretKey(keySize);
    hr = PFEntityGetSecretKey(entityHandle, secretKey.size(), secretKey.data(), nullptr);
}
```

> [!NOTE]
>
> **PFEntityGetSecretKey** 僅適用於 Windows、Linux 和 macOS 平台。

## 權杖事件處理常式

SDK 會在實體權杖到期之前自動重新整理。您可以註冊回呼來觀察這些事件。

### 權杖已到期

如果自動重新整理失敗 (例如，原始登入認證已不再有效)，SDK 會引發權杖到期事件。請註冊處理常式以提供新的認證並重試登入。

```cpp theme={null}
PFRegistrationToken registrationToken{};
HRESULT hr = PFEntityRegisterTokenExpiredEventHandler(
    nullptr,  // XTaskQueueHandle, or nullptr for default
    nullptr,  // optional context
    [](void* ctx, PFEntityKey const* entityKey)
    {
        // Re-authenticate the player with a fresh credential
        // See relogin.md for a complete example
    },
    &registrationToken);
```

### 權杖已重新整理

如果您想知道 SDK 何時在背景中成功重新整理權杖，請註冊權杖已重新整理處理常式。這僅供參考，您不需要採取任何動作。

```cpp theme={null}
PFRegistrationToken registrationToken{};
HRESULT hr = PFEntityRegisterTokenRefreshedEventHandler(
    nullptr,  // XTaskQueueHandle, or nullptr for default
    nullptr,  // optional context
    [](void* ctx, PFEntityKey const* entityKey, const PFEntityToken* newToken)
    {
        // Log the refresh or update cached token references
    },
    &registrationToken);
```

### 何時註冊和取消註冊

* 請及早**註冊**處理常式，也就是在 SDK 初始化之後、登入之前。這可確保您不會遺漏任何事件。
* 請在關閉期間、關閉實體控制代碼之前**取消註冊**處理常式。

```cpp theme={null}
PFEntityUnregisterTokenExpiredEventHandler(expiredRegistrationToken);
PFEntityUnregisterTokenRefreshedEventHandler(refreshedRegistrationToken);
```

如需權杖到期處理和重新登入的完整逐步解說，請參閱[處理權杖到期](https://devdocs.xbox.com/zh-TW/services/playfab/sdks/c/relogin.md)。

## 公用程式存取子

您可以從實體控制代碼擷取 API 端點和遊戲識別碼。這些值來自登入期間所使用的 **PFServiceConfigHandle**。

```cpp theme={null}
// Get API endpoint
size_t endpointSize{};
HRESULT hr = PFEntityGetAPIEndpointSize(entityHandle, &endpointSize);

std::vector<char> endpoint(endpointSize);
hr = PFEntityGetAPIEndpoint(entityHandle, endpoint.size(), endpoint.data(), nullptr);

// Get title ID
size_t titleIdSize{};
hr = PFEntityGetTitleIdSize(entityHandle, &titleIdSize);

std::vector<char> titleId(titleIdSize);
hr = PFEntityGetTitleId(entityHandle, titleId.size(), titleId.data(), nullptr);
```

## 完整範例

此範例示範如何建立、複製、查詢和關閉實體控制代碼：

```cpp theme={null}
#include <playfab/core/PFEntity.h>
#include <playfab/services/PFServices.h>

void EntityHandleExample(PFServiceConfigHandle serviceConfigHandle, XUserHandle userHandle)
{
    //
    // Log in and get an entity handle
    //
    PFAuthenticationLoginWithXUserRequest request{};
    request.createAccount = true;
    request.user = userHandle;

    XAsyncBlock asyncLogin{};
    HRESULT hr = PFAuthenticationLoginWithXUserAsync(serviceConfigHandle, &request, &asyncLogin);
    hr = XAsyncGetStatus(&asyncLogin, true);

    PFEntityHandle entityHandle{ nullptr };
    size_t resultSize{};
    hr = PFAuthenticationLoginWithXUserGetResultSize(&asyncLogin, &resultSize);

    std::vector<char> loginBuffer(resultSize);
    PFAuthenticationLoginResult const* loginResult{};
    hr = PFAuthenticationLoginWithXUserGetResult(
        &asyncLogin, &entityHandle,
        loginBuffer.size(), loginBuffer.data(),
        &loginResult, nullptr);

    //
    // Register token event handlers
    //
    PFRegistrationToken expiredToken{};
    hr = PFEntityRegisterTokenExpiredEventHandler(nullptr, nullptr,
        [](void* ctx, PFEntityKey const* entityKey)
        {
            // Handle re-authentication
        }, &expiredToken);

    PFRegistrationToken refreshedToken{};
    hr = PFEntityRegisterTokenRefreshedEventHandler(nullptr, nullptr,
        [](void* ctx, PFEntityKey const* entityKey, const PFEntityToken* newToken)
        {
            // Log token refresh
        }, &refreshedToken);

    //
    // Duplicate the handle for a subsystem
    //
    PFEntityHandle subsystemHandle{ nullptr };
    hr = PFEntityDuplicateHandle(entityHandle, &subsystemHandle);

    //
    // Query entity info
    //
    bool isTitlePlayer{};
    hr = PFEntityIsTitlePlayer(entityHandle, &isTitlePlayer);

    size_t keySize{};
    hr = PFEntityGetEntityKeySize(entityHandle, &keySize);

    std::vector<char> keyBuffer(keySize);
    PFEntityKey const* entityKey{};
    hr = PFEntityGetEntityKey(entityHandle, keyBuffer.size(), keyBuffer.data(), &entityKey, nullptr);

    //
    // ... make service calls with entityHandle or subsystemHandle ...
    //

    //
    // Cleanup: unregister handlers, then close all handles
    //
    PFEntityUnregisterTokenExpiredEventHandler(expiredToken);
    PFEntityUnregisterTokenRefreshedEventHandler(refreshedToken);

    PFEntityCloseHandle(subsystemHandle);
    PFEntityCloseHandle(entityHandle);
}
```

## API 參考

| 函式 | 描述 |
| - | - |
| [PFEntityDuplicateHandle](https://devdocs.xbox.com/zh-TW/services/playfab/api-references/c/pfentity/functions/pfentityduplicatehandle.md) | 複製控制代碼，並遞增參考計數。這兩個控制代碼都必須分別關閉。 |
| [PFEntityCloseHandle](https://devdocs.xbox.com/zh-TW/services/playfab/api-references/c/pfentity/functions/pfentityclosehandle.md) | 關閉控制代碼，並遞減參考計數。當最後一個控制代碼關閉時，實體會被終結。 |
| [PFEntityGetEntityKeySize](https://devdocs.xbox.com/zh-TW/services/playfab/api-references/c/pfentity/functions/pfentitygetentitykeysize.md) | 取得儲存實體索引鍵所需的緩衝區大小。 |
| [PFEntityGetEntityKey](https://devdocs.xbox.com/zh-TW/services/playfab/api-references/c/pfentity/functions/pfentitygetentitykey.md) | 取得實體的實體索引鍵 (類型和識別碼)。 |
| [PFEntityGetEntityTokenAsync](https://devdocs.xbox.com/zh-TW/services/playfab/api-references/c/pfentity/functions/pfentitygetentitytokenasync.md) | 以非同步方式擷取快取的實體權杖。 |
| [PFEntityGetEntityTokenResultSize](https://devdocs.xbox.com/zh-TW/services/playfab/api-references/c/pfentity/functions/pfentitygetentitytokenresultsize.md) | 取得實體權杖結果所需的緩衝區大小。 |
| [PFEntityGetEntityTokenResult](https://devdocs.xbox.com/zh-TW/services/playfab/api-references/c/pfentity/functions/pfentitygetentitytokenresult.md) | 從已完成的 PFEntityGetEntityTokenAsync 呼叫取得實體權杖。 |
| [PFEntityIsTitlePlayer](https://devdocs.xbox.com/zh-TW/services/playfab/api-references/c/pfentity/functions/pfentityistitleplayer.md) | 傳回實體是否為 title\_player\_account。 |
| [PFEntityGetSecretKeySize](https://devdocs.xbox.com/zh-TW/services/playfab/api-references/c/pfentity/functions/pfentitygetsecretkeysize.md) | 取得祕密金鑰所需的緩衝區大小。如果實體不是遊戲實體，則會失敗。 |
| [PFEntityGetSecretKey](https://devdocs.xbox.com/zh-TW/services/playfab/api-references/c/pfentity/functions/pfentitygetsecretkey.md) | 取得遊戲實體的祕密金鑰。僅適用於 Windows、Linux 和 macOS。 |
| [PFEntityGetAPIEndpointSize](https://devdocs.xbox.com/zh-TW/services/playfab/api-references/c/pfentity/functions/pfentitygetapiendpointsize.md) | 取得 API 端點字串所需的緩衝區大小。 |
| [PFEntityGetAPIEndpoint](https://devdocs.xbox.com/zh-TW/services/playfab/api-references/c/pfentity/functions/pfentitygetapiendpoint.md) | 從實體相關聯的服務設定取得 API 端點。 |
| **PFEntityGetTitleIdSize** | 取得遊戲識別碼字串所需的緩衝區大小。 |
| **PFEntityGetTitleId** | 從實體相關聯的服務設定取得遊戲識別碼。 |
| [PFEntityRegisterTokenExpiredEventHandler](https://devdocs.xbox.com/zh-TW/services/playfab/api-references/c/pfentity/functions/pfentityregistertokenexpiredeventhandler.md) | 註冊在自動權杖重新整理失敗時的回呼。 |
| [PFEntityUnregisterTokenExpiredEventHandler](https://devdocs.xbox.com/zh-TW/services/playfab/api-references/c/pfentity/functions/pfentityunregistertokenexpiredeventhandler.md) | 取消註冊權杖到期回呼。 |
| [PFEntityRegisterTokenRefreshedEventHandler](https://devdocs.xbox.com/zh-TW/services/playfab/api-references/c/pfentity/functions/pfentityregistertokenrefreshedeventhandler.md) | 註冊在 SDK 成功重新整理權杖時的回呼。 |
| [PFEntityUnregisterTokenRefreshedEventHandler](https://devdocs.xbox.com/zh-TW/services/playfab/api-references/c/pfentity/functions/pfentityunregistertokenrefreshedeventhandler.md) | 取消註冊權杖已重新整理回呼。 |

如需完整的 API 參考，請參閱 [PFEntity 成員](https://devdocs.xbox.com/zh-TW/services/playfab/api-references/c/pfentity/pfentity_members.md)。

## 另請參閱

* [處理權杖到期](https://devdocs.xbox.com/zh-TW/services/playfab/sdks/c/relogin.md)
* [使用遊戲實體存取 PlayFab](https://devdocs.xbox.com/zh-TW/services/playfab/sdks/c/server.md)
* [SDK 生命週期](https://devdocs.xbox.com/zh-TW/services/playfab/sdks/c/lifecycle.md)
* [快速入門：Windows](https://devdocs.xbox.com/zh-TW/services/playfab/sdks/c/quickstart-gdk.md)
* [快速入門：Win32](https://devdocs.xbox.com/zh-TW/services/playfab/sdks/c/quickstart-win32.md)


## Related topics

- [來自遊戲實體的遊戲伺服器](https://devdocs.xbox.com/zh-TW/services/playfab/sdks/c/server.md)
- [PFEntityDuplicateHandle](https://devdocs.xbox.com/zh-TW/services/playfab/api-references/c/pfentity/functions/pfentityduplicatehandle.md)
- [事件管線教學課程](https://devdocs.xbox.com/zh-TW/services/playfab/sdks/c/event-pipeline/eventpipeline-tutorial.md)
- [快速入門 (Windows) - 呼叫 PlayFab 服務](https://devdocs.xbox.com/zh-TW/services/playfab/sdks/unified-sdk/quickstart-services.md)
- [移轉指南：PlayFab 獨立 SDK (v1) 至 PlayFab Unified SDK (v2)](https://devdocs.xbox.com/zh-TW/services/playfab/sdks/unified-sdk/migrating-from-v1.md)
