> ## 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 Services SDK - 事件管線

> 使用 PlayFab Services SDK 事件管線傳送批次處理的遙測和 PlayStream 事件：驗證類型、設定、GZIP 壓縮和處理常式。

事件管線是 PlayFab Services SDK 的一項功能，其主要用途是讓遊戲開發人員傳送事件，以儲存在 PlayFab Insights 中。它可讓開發人員指定批次大小、傳送頻率，以及完善遙測解決方案的其他層面。

它有助於減輕遊戲開發人員的負擔，並代替他們處理所有這些層面。它支援可設定的事件管線。一個遊戲可以包含一或多個這類管線，而每個管線都可以使用自己的屬性進行設定。

讓我們來看看管線的一些基本概念。

## 管線類型

管線類型與管線所發出的事件類型直接相關。開發人員可以具現化兩種不同類型的管線：

* **遙測事件管線**：只能發出遙測事件，並使用 [Write Telemetry Events](https://learn.microsoft.com/en-us/rest/api/playfab/events/play-stream-events/write-telemetry-events) REST API。

* **PlayStream 事件管線**：只能發出 PlayStream 事件，並使用 [Write Events](https://learn.microsoft.com/en-us/rest/api/playfab/events/play-stream-events/write-events) REST API。

您可以擁有多個不同類型和設定的管線，但在建立之後**無法**變更管線類型。變更類型需要重新具現化管線。

## 驗證類型

建立管線的另一個重要部分是驗證類型。PlayFab 事件支援兩種驗證機制：實體驗證和遙測金鑰驗證。

當遊戲開發人員想要將事件連結至特定實體以進行進一步彙總或分析時，會使用實體驗證。例如，遊戲開發人員可以記錄與玩家動作相關的事件，以對遊戲玩家進行可供分群的進一步行為分析。

當遊戲開發人員想要記錄不一定需要連結至實體的事件時，會使用遙測金鑰驗證。例如，在有已登入的玩家之前，遊戲就可以開始傳送與效能相關的事件，或想要收集以供進一步分析的特定計量。這不需要經過一般的實體驗證程序即可完成。

**支援的驗證類型 / 事件類型組合**

| | **實體驗證** | **遙測金鑰驗證** |
| - | :-: | :-: |
| **遙測事件** | 有效 | 有效 |
| **PlayStream 事件** | 有效 | 無效 |

### 實體驗證

此驗證類型是一般且最常見的 PlayFab 驗證方法。它與特定實體緊密相關，並要求您呼叫對應的 PlayFab 登入 API 來取得實體權杖，以用於任何後續呼叫。下列清單列出可搭配實體驗證使用的不同實體類型。

* **namespace**：命名空間實體代表跨遊戲的玩家身分識別和共用資料範圍。發行者識別碼會識別命名空間。
* **title**：遊戲實體代表範圍限於該遊戲的全域資訊。
* **master\_player\_account**：`master_player_account` 是命名空間中所有遊戲共用的玩家實體。
* **title\_player\_account**：對大多數開發人員而言，title\_player\_account 以最傳統的方式代表玩家。
* **character**：角色實體是 title\_player\_account 的子實體。
* **group**：群組實體是其他實體的容器。目前僅限於玩家和角色。

如需不同實體類型的詳細資訊，請參閱[內建實體類型](/zh-TW/services/playfab/live-service-management/game-configuration/entities/available-built-in-entity-types)。

此外，在建立管線之後，也可以提供有效的 **PFEntityHandle** 來更新管線實體。因此，遊戲開發人員可以新增實體，以開始將事件連結至該實體 (*請參閱[切換至實體驗證或更新實體](#switching-to-entity-auth-or-updating-entity)一節*)，或者如果想要記錄與實體無關的內容，甚至可以將其移除 (*請參閱[切換至遙測金鑰驗證](#switching-to-telemetry-key-auth)一節*)。

如果管線上有實體存在，它一律會優先於遙測金鑰驗證。

### 遙測金鑰驗證

遙測金鑰驗證不需要實體權杖，因此不會與任何特定實體綁定。

如果使用遙測金鑰驗證，在建立管線期間需要 **PFEventPipelineTelemetryKeyConfig** 結構。此結構有兩個主要部分：

1. 遙測金鑰，這是一個透過 PlayFab Game Manager 建立和管理的字串。

2. **PFServiceConfigHandle**，可讓 SDK 知道應使用哪個正確的服務設定來上傳事件。服務設定控制代碼是在 SDK 初始化期間呼叫 [**PFServiceConfigCreateHandle**](/zh-TW/services/playfab/api-references/c/pfserviceconfig/functions/pfserviceconfigcreatehandle) 建立的。

如果開發人員想要使用遙測金鑰，請務必在建立管線時提供，因為在管線具現化之後就無法再新增遙測金鑰。

值得一提的是，**遙測金鑰驗證僅適用於遙測事件**；它不支援 PlayStream 事件。

## 事件管線設定

如前所述，事件管線有一些可設定的屬性，可在建立管線之後透過 **PFEventPipelineConfig** 結構參數提供。

可設定的屬性如下：

* **maxEventsPerBatch**：在將事件寫入 PlayFab 之前批次處理的最大事件數目。
* **maxWaitTimeInSeconds**：管線在送出不完整批次之前等候的最長時間。
* **pollDelayInMs**：管線在清空事件緩衝區之後，再次從中讀取之前等候的時間。
* **compressionLevel**：定義壓縮演算法所使用的壓縮層級。如需壓縮的詳細資訊，請參閱 *[GZIP 壓縮](#gzip-compression)*。
* **retryOnDisconnect**：事件管線會重試傳送因連線中斷而失敗的事件。僅適用於遙測事件管線。
* **bufferSize**：管線緩衝區中事件數量的限制。

### 預設值

| 屬性 | 遙測管線 | PlayStream 管線 |
| - | - | - |
| **maxEventsPerBatch** | 5 | 5 |
| **maxWaitTimeInSeconds** | 3 | 3 |
| **pollDelayInMs** | 3000 | 10 |
| **compressionLevel** | None (已停用) | None (已停用) |
| **retryOnDisconnect** | true | N/A |
| **bufferSize** | 1024 | 1024 |

如果 [**PFEventPipelineConfig**](/zh-TW/services/playfab/api-references/c/pfeventpipeline/structs/pfeventpipelineconfig) 只指定了部分屬性，空白的屬性會被覆寫並使用預設值。

如需如何為事件管線更新這些屬性的範例，請參閱 *[更新管線設定](#update-pipeline-configuration)*。

<a id="gzip-compression" />

#### **GZIP 壓縮**

事件管線提供使用 GZIP 壓縮標準來壓縮本文承載的選項。

您可以在 **PFEventPipelineConfig** 結構中指定所需的壓縮層級，此結構是 [**PFEventPipelineUpdateConfiguration**](/zh-TW/services/playfab/api-references/c/pfeventpipeline/functions/pfeventpipelineupdateconfiguration) API 參數的一部分。

較低的壓縮層級壓縮程度較低，但速度最快；較高的壓縮層級可達到較佳的壓縮率，但壓縮速度最慢。壓縮率的差異完全取決於所傳送的資料類型。依資料的大小和隨機性而定，即使在不同層級，壓縮率也可能相同。

**取捨：**

由於執行壓縮演算法增加了複雜度，使用壓縮會增加 CPU 時間，但另一方面，網路本文承載會大幅減少。因此，是否應使用壓縮取決於遊戲開發人員依據遊戲需求和資源所做的決定。

根據內部驗證，CPU 時間平均增加 20%，而承載本文大小平均減少 91%。這些百分比可能會因承載大小和所壓縮資料的複雜度而有很大差異。

## 事件處理常式

在建立管線時，遊戲開發人員可以提供兩個選擇性的事件處理常式，這些處理常式會在上傳事件時叫用。

不過，如果遊戲開發人員想要「射後不理」的體驗，可以省略提供事件處理常式。

可提供的事件處理常式如下：

* [**PFEventPipelineBatchUploadSucceededEventHandler**](/zh-TW/services/playfab/api-references/c/pfeventpipeline/functions/pfeventpipelinebatchuploadsucceededeventhandler)：顧名思義，它會接收所有成功上傳至 PlayFab 的事件。

* [**PFEventPipelineBatchUploadFailedEventHandler**](/zh-TW/services/playfab/api-references/c/pfeventpipeline/functions/pfeventpipelinebatchuploadfailedeventhandler)：它會在經過管線重試邏輯之後，接收所有失敗的事件。

### 管線建立範例

以下根據先前討論的主題，提供如何具現化事件管線的不同範例。

1. **使用實體驗證建立遙測事件管線**

   如果開發人員想要傳送遙測事件，而且不需要使用遙測金鑰驗證，可以使用 [**PFEventPipelineCreateTelemetryPipelineHandleWithEntity**](/zh-TW/services/playfab/api-references/c/pfeventpipeline/functions/pfeventpipelinecreatetelemetrypipelinehandlewithentity) API，如下一個範例所示：

   ```cpp theme={null}
   void EventPipelineCreation(PFEntityHandle entityHandle, XTaskQueueHandle taskQueueHandle)
   {
       PFEventPipelineHandle handle;

       HRESULT hr = PFEventPipelineCreateTelemetryPipelineHandleWithEntity(
           entityHandle,                       // entityHandle
           taskQueueHandle,                    // queue
           nullptr,                            // eventPipelineBatchUploadedEventHandler
           nullptr,                            // eventPipelineBatchFailedEventHandler
           nullptr,                            // handlerContext
           &handle                             // eventPipelineHandle
       );

       if (FAILED(hr))
       {
           printf("Failed creating event pipeline: 0x%x\r\n", hr);
           return;
       }
   }
   ```

   此範例示範如何建立使用實體驗證且沒有處理常式的遙測事件管線。這表示管線會發出事件，而不理會結果。

2. **使用遙測金鑰驗證建立遙測事件管線**

   如果開發人員想要傳送遙測事件，而且需要使用遙測金鑰驗證，可以使用 PFEventPipelineCreateTelemetryPipelineHandleWithKey API，如下一個範例所示：

   ```cpp theme={null}
   void EventPipelineCreation(PFServiceConfigHandle serviceConfigHandle, XTaskQueueHandle taskQueueHandle)
   {
       PFEventPipelineHandle handle;

       PFEventPipelineTelemetryKeyConfig telemetryKeyConfig
       {
           "myTelemetryKey",                   // telemetryKey
           serviceConfigHandle,                // serviceConfigHandle
       };

       HRESULT hr = PFEventPipelineCreateTelemetryPipelineHandleWithKey(
           &telemetryKeyConfig,                // eventPipelineTelemetryKeyConfig
           taskQueueHandle,                    // queue
           nullptr,                            // eventPipelineBatchUploadedEventHandler
           nullptr,                            // eventPipelineBatchFailedEventHandler
           nullptr,                            // handlerContext
           &handle                             // eventPipelineHandle
       );

       if (FAILED(hr))
       {
           printf("Failed creating event pipeline: 0x%x\r\n", hr);
           return;
       }
   }
   ```

   此範例示範如何建立不使用實體驗證的遙測事件管線。

   事件管線會開始使用遙測金鑰上傳事件，之後也可以新增實體。請參閱[切換至實體驗證或更新實體](#switching-to-entity-auth-or-updating-entity)

3. **建立 PlayStream 事件管線**

   如果開發人員想要傳送 PlayStream 事件，可以使用 PFEventPipelineCreatePlayStreamPipelineHandle API，如下一個範例所示：

   ```cpp theme={null}
   void EventPipelineCreation(PFEntityHandle entityHandle, XTaskQueueHandle taskQueueHandle)
   {
       PFEventPipelineHandle handle;

       HRESULT hr = PFEventPipelineCreatePlayStreamPipelineHandle(
           entityHandle,                       // entityHandle
           taskQueueHandle,                    // queue
           nullptr,                            // eventPipelineBatchUploadedEventHandler
           nullptr,                            // eventPipelineBatchFailedEventHandler
           nullptr,                            // handlerContext
           &handle                             // eventPipelineHandle
       );

       if (FAILED(hr))
       {
           printf("Failed creating event pipeline: 0x%x\r\n", hr);
           return;
       }
   }
   ```

   此範例示範如何建立使用實體驗證且沒有處理常式的 PlayStream 事件管線。這表示管線會發出事件，而不理會結果。

4. **使用事件處理常式的事件管線**

   此範例示範如何將事件處理常式提供給管線建立 API。

   ```cpp theme={null}
   void CALLBACK OnBatchUploadedHandler(void* context, PFUploadedEvent const* const* events, size_t eventsCount)
   {
       // Handle response
   }

   void CALLBACK OnBatchUploadFailedHandler(void* context, HRESULT hr, const char* errorMessage, PFEvent const* const* events, size_t eventsCount)
   {
       // Handle response
   }

   void EventPipelineCreation(PFEntityHandle entityHandle, XTaskQueueHandle taskQueueHandle)
   {
       PFEventPipelineHandle handle;

       HRESULT hr = PFEventPipelineCreateTelemetryPipelineHandleWithEntity(
           entityHandle,                       // entityHandle
           taskQueueHandle,                    // queue
           OnBatchUploadedHandler,             // eventPipelineBatchUploadedEventHandler
           OnBatchUploadFailedHandler,         // eventPipelineBatchFailedEventHandler
           nullptr,                            // handlerContext
           &handle                             // eventPipelineHandle
       );

       if (FAILED(hr))
       {
           printf("Failed creating event pipeline: 0x%x\r\n", hr);
           return;
       }
   }
   ```

如範例所示，***OnBatchUploadedHandler*** 和 ***OnBatchUploadFailedHandler*** 被宣告為兩個不同的回呼函式，並在建立事件管線時傳遞。與事件相關聯的每個結果都會在這兩個函式之一中傳回，視結果是成功還是失敗而定。如何處理結果由遊戲開發人員決定。

### 發出事件

一旦建立了事件管線，發出事件就是很直接的作業。遊戲開發人員應呼叫 [**PFEventPipelineEmitEvent**](/zh-TW/services/playfab/api-references/c/pfeventpipeline/functions/pfeventpipelineemitevent) API，此 API 會接收現有的事件管線控制代碼和要傳送的事件。

```cpp theme={null}
void EmitEvent(PFEventPipelineHandle handle)
{
    PFEvent myEvent
    {
        nullptr,
        "custom.playfab.events.PlayFab.Test.TelemetryEventPipelineTests",
        "TelemetryEvent",
        nullptr,
        "{}"
    };

    HRESULT hr = PFEventPipelineEmitEvent(
        handle,
        &myEvent
    );

    if (FAILED(hr))
    {
        printf("Failed emitting event: 0x%x\r\n", hr);
        return;
    }
}
```

請務必注意，使用遙測金鑰驗證時，**PFEntityKey** 中 entityType 唯一有效的值是 **"external"**。任何其他實體類型都會遭到拒絕，且事件不會上傳。

使用遙測金鑰驗證時有效 PFEntityKey 的範例：

```cpp theme={null}
void EmitEvent(PFEventPipelineHandle handle)
{
    PFEntityKey pfEntityKey{ "my-unique-ID", "external" };  // Note the "external" value

    PFEvent myEvent
    {
        &pfEntityKey,
        "custom.playfab.events.PlayFab.Test.EventsWithTelemetryKey",
        "TelemetryEvent",
        nullptr,
        "{}"
    };

    HRESULT hr = PFEventPipelineEmitEvent(
        handle,
        &myEvent
    );

    if (FAILED(hr))
    {
        printf("Failed emitting event: 0x%x\r\n", hr);
        return;
    }
}
```

如果發出事件時超過緩衝區大小限制，您會從 SDK 收到如下錯誤：

* `E_PF_API_CLIENT_REQUEST_RATE_LIMIT_EXCEEDED (0x892354dd)`

請務必根據您的需求設定適當的緩衝區大小。

<a id="switching-to-entity-auth-or-updating-entity" />

### 切換至實體驗證或更新實體

如果管線僅使用遙測金鑰設定建立，有一種方式可以切換至實體驗證，或者如果您想要更新管線以使用不同的實體，也可以這麼做。

透過使用 [**PFEventPipelineAddUploadingEntity**](/zh-TW/services/playfab/api-references/c/pfeventpipeline/functions/pfeventpipelineadduploadingentity)，您可以將實體附加至執行中的管線，而不需要重新初始化管線。如果已有實體，它也會取代現有的實體。

範例：

在遊戲開始執行時，使用遙測金鑰設定建立遙測事件管線。可能的原因是尚未有玩家身分識別，或您還不想記錄任何與玩家相關的內容。

```cpp theme={null}
void EventPipelineCreation(PFServiceConfigHandle serviceConfigHandle, XTaskQueueHandle taskQueueHandle)
{
    PFEventPipelineHandle handle;

    PFEventPipelineTelemetryKeyConfig telemetryKeyConfig
    {
        "myTelemetryKey",                   // telemetryKey
        serviceConfigHandle,                // serviceConfigHandle
    };

    HRESULT hr = PFEventPipelineCreateTelemetryPipelineHandleWithKey(
        &telemetryKeyConfig,                // eventPipelineTelemetryKeyConfig
        taskQueueHandle,                    // queue
        nullptr,                            // eventPipelineBatchUploadedEventHandler
        nullptr,                            // eventPipelineBatchFailedEventHandler
        nullptr,                            // handlerContext
        &handle                             // eventPipelineHandle
    );

    if (FAILED(hr))
    {
        printf("Failed creating event pipeline: 0x%x\r\n", hr);
        return;
    }
}
```

在玩家登入之後，或當您想要開始傳送附加至特定實體的事件時，請使用現有的事件管線控制代碼和所需的實體控制代碼呼叫 **PFEventPipelineAddUploadingEntity**，如下所示：

```cpp theme={null}
void EventPipelineUpdateEntity(PFEventPipelineHandle handle)
{
    HRESULT hr = PFEventPipelineAddUploadingEntity(
        handle,                 // eventPipelineHandle
        entityHandle            // entityHandle
    );

    if (FAILED(hr))
    {
        printf("Failed adding uploading entity: 0x%x\r\n", hr);
        return;
    }
}
```

此呼叫之後，管線會開始記錄任何連結至該實體的後續事件。

<a id="switching-to-telemetry-key-auth" />

### 切換至遙測金鑰驗證

延伸先前的案例，如果開發人員想要回到遙測驗證，並將事件記錄與實體分離，可以呼叫 [**PFEventPipelineRemoveUploadingEntity**](/zh-TW/services/playfab/api-references/c/pfeventpipeline/functions/pfeventpipelineremoveuploadingentity) 並傳遞事件管線控制代碼，如下所示：

```cpp theme={null}
void EventPipelineRemoveEntity(PFEventPipelineHandle handle)
{
    HRESULT hr = PFEventPipelineRemoveUploadingEntity(
        handle                  // eventPipelineHandle
    );

    if (FAILED(hr))
    {
        printf("Failed removing uploading entity: 0x%x\r\n", hr);
        return;
    }
}
```

此呼叫會移除實體，並實際上針對所有後續事件切換回遙測金鑰驗證。

<a id="update-pipeline-configuration" />

### **更新管線設定**

您可以使用 [**PFEventPipelineUpdateConfiguration**](/zh-TW/services/playfab/api-references/c/pfeventpipeline/functions/pfeventpipelineupdateconfiguration) API 更新管線，此 API 會接收現有的事件管線控制代碼和新的設定結構，如下所示：

```cpp theme={null}
void EventPipelineUpdateConfiguration(PFEventPipelineHandle handle)
{
    uint32_t maxEvents = 10;
    uint32_t maxWaitTime = 5;
    uint32_t pollDelay = 50;
    PFHCCompressionLevel compressionLevel = PFHCCompressionLevel::Medium;
    bool retryOnDisconnect = true;
    size_t bufferSize = 2048;

    PFEventPipelineConfig eventPipelineConfig
    {
        &maxEvents,             // maxEventsPerBatch
        &maxWaitTime,           // maxWaitTimeInSeconds
        &pollDelay,             // pollDelayInMs
        &compressionLevel,      // compressionLevel
        &retryOnDisconnect,     // retryOnDisconnect
        &bufferSize,            // bufferSize
    };

    HRESULT hr = PFEventPipelineUpdateConfiguration(
        handle,                 // eventPipelineHandle
        eventPipelineConfig     // eventPipelineConfig
    );

    if (FAILED(hr))
    {
        printf("Failed updating event pipeline configuration: 0x%x\r\n", hr);
        return;
    };
}
```

***提醒**：任何空白或 null 屬性都會以預設值覆寫現有的設定值。*

## 無效 / 已停用的遙測金鑰

如果遙測金鑰已停用，或在建立管線時提供了無效的金鑰，任何使用該遙測金鑰執行的事件管線一旦發現金鑰無效或已停用，就會開始失敗。

即使客戶最終重新啟用了金鑰，管線也不會察覺，並會繼續透過失敗事件處理常式傳回失敗。值得一提的是，此行為是以工作階段為基礎的，這表示如果重新啟動遊戲，就會建立新的管線，並能再次上傳事件。

## 管線控制代碼生命週期

與其他 PlayFab 控制代碼一樣，事件管線控制代碼也使用複製/關閉模式：

* [**PFEventPipelineCloseHandle**](/zh-TW/services/playfab/api-references/c/pfeventpipeline/functions/pfeventpipelineclosehandle)：關閉管線控制代碼。當管線的最後一個控制代碼關閉時，管線會被終結，並排清任何剩餘的已緩衝事件。
* [**PFEventPipelineDuplicateHandle**](/zh-TW/services/playfab/api-references/c/pfeventpipeline/functions/pfeventpipelineduplicatehandle)：複製管線控制代碼。原始控制代碼和複製的控制代碼都必須使用 [**PFEventPipelineCloseHandle**](/zh-TW/services/playfab/api-references/c/pfeventpipeline/functions/pfeventpipelineclosehandle) 分別關閉。

```cpp theme={null}
// Duplicate a handle for use in another component
PFEventPipelineHandle duplicatedHandle;
HRESULT hr = PFEventPipelineDuplicateHandle(originalHandle, &duplicatedHandle);

// When done, close both handles independently
PFEventPipelineCloseHandle(duplicatedHandle);
PFEventPipelineCloseHandle(originalHandle);
```

## 另請參閱

* [事件管線教學課程](/zh-TW/services/playfab/sdks/c/event-pipeline/eventpipeline-tutorial)


## Related topics

- [事件管線教學課程](/zh-TW/services/playfab/sdks/c/event-pipeline/eventpipeline-tutorial.md)
- [PlayFab Services C/C++ SDK 版本資訊](/zh-TW/services/playfab/release-notes/playfab-c/index.md)
- [PlayFab Services SDK](/zh-TW/services/playfab/sdks/c/index.md)
- [PlayFab Services SDK 2023 版本資訊](/zh-TW/services/playfab/release-notes/2023.md)
- [PlayFab 支援的語言](/zh-TW/services/playfab/sdks/languages/index.md)
