> ## 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 Unreal Engine Marketplace 外掛程式

> 如何將 PlayFab SDK UE Marketplace 外掛程式整合到已整合 PlayFab Online Subsystem 之現有專案的指引。

# 快速入門指南：將 PlayFab Online Subsystem 與 PlayFab Unreal Engine Marketplace 外掛程式整合

本快速入門指南可協助您將 [PlayFab SDK Unreal Engine (UE) Marketplace 外掛程式](/zh-TW/services/playfab/sdks/unreal)整合到已整合 PlayFab Online Subsystem (OSS) 的現有專案中。

PlayFab SDK 外掛程式用於執行 PlayFab 管理、用戶端和伺服器作業，例如玩家驗證、虛擬物品和貨幣管理，以及社交功能建立。與 PlayFab Online Subsystem 搭配使用時，您的專案就能存取適用於 Unreal Engine 專案的完整 PlayFab 功能套件。

個別使用 PlayFab OSS 或 PlayFab SDK 外掛程式時，第一個步驟都是驗證使用者。如果在同一個專案中同時使用兩者而沒有適當的設定，PlayFab OSS 和 PlayFab SDK 外掛程式都會為同一位使用者進行個別的驗證呼叫。

本快速入門指南可協助您透過 PlayFab OSS 進行驗證，並在 PlayFab SDK 外掛程式中重複使用該驗證，以提供順暢的使用者驗證體驗。

## 先決條件

* 確定您已在 `<game>.build.cs` 檔案中將 PlayFab OSS 整合為外掛程式。在下列範例中，我們建立一個切換選項來允許整合，並設定一個定義以允許 `#ifdef` 保護。
  ```cs theme={null}
  // PlayFab OnlineSubsystem Plugin Settings.
  //
  // While the PlayFab OSS is integrated with the game at an engine level, we cannot directly access
  // PF OSS headers unless we integrate it as a plugin.
  //
  // `bOSSPlayFabAsPlugin` must be set to true to expose the PlayFab OSS headers directly to the game.
  bool bOSSPlayFabAsPlugin = false;
  if (bOSSPlayFabAsPlugin)
  {
      PublicDependencyModuleNames.AddRange(new string[] { "OnlineSubsystemPlayFab" });
      PublicDefinitions.Add("WITH_OSS_PLAYFAB=1");
  }
  ```

* 確定您已在 `<game>.build.cs` 檔案中整合 PlayFab SDK 外掛程式。在下列範例中，我們建立一個切換選項來允許整合，並設定一個定義以允許 `#ifdef` 保護。
  ```cs theme={null}
  // PlayFab SDK Plugin Settings.
  //
  // `bPlayFabPlugin` must be set to true to enable compilation and integration with the PlayFab
  // SDK Plugin.
  // Ensure that the PF SDK Plugin is installed via Engine plugin or as a local game plugin.
  bool bPlayFabPlugin = false;
  if (bPlayFabPlugin)
  {
      PrivateDependencyModuleNames.AddRange(new string[] { "PlayFab", "PlayFabCpp", "PlayFabCommon" });
      PublicDefinitions.Add("WITH_PLAYFAB_PLUGIN=1");
  }
  ```

* 更新 `<game.build.cs>` 檔案之後 (以及之後每次更新時)，請務必以滑鼠右鍵按一下 `.uproject` 檔案並選取 `Generate Visual Studio Project Files`，以重新產生遊戲的 Visual Studio 專案檔。

* PlayFab OSS 現在提供公用的 `AuthenticateUserComplete` 委派，會在 PlayFab OSS 使用者驗證流程完成時觸發。繫結至此委派，以擷取 PlayFab OSS 驗證的詳細資料。
  ```cpp theme={null}
  /**
  * Delegate used on the completion of FOnlineIdentityPlayFab::AuthenticateUser().
  *
  * @param LocalUserNum the controller number of the associated user that made the request
  * @param bWasSuccessful true if authentication was successful, false if there was an error
  * @param PlatformUserIdStr user identifier returned from the service.
  * @param ErrorStr string representing the error condition
  */
  DECLARE_MULTICAST_DELEGATE_FourParams(FOnAuthenticateUserComplete, int32 /*LocalUserNum*/, bool /*bWasSuccessful*/, const FString& /*PlatformUserIdStr*/, const FString& /*ErrorStr*/);
  typedef FOnAuthenticateUserComplete::FDelegate FOnAuthenticateUserCompleteDelegate;
  ```

## 步驟

您也可以直接跳至[完整程式碼範例](#full-code-example)。

1. **包含正確的標頭**
   ```cpp theme={null}
   #ifdef WITH_OSS_PLAYFAB
   #include "OnlineSubsystemPlayFab.h"
   #endif // WITH_OSS_PLAYFAB

   #ifdef WITH_PLAYFAB_PLUGIN
   #include "PlayFab.h"
   #include "PlayFabCommon.h"
   #include "Core/PlayFabClientDataModels.h"
   #endif // WITH_PLAYFAB_PLUGIN
   ```

2. **擷取 PlayFab OSS**
   ```cpp theme={null}
   FOnlineSubsystemPlayFab* PFOnlineSub = static_cast<FOnlineSubsystemPlayFab*>(IOnlineSubsystem::Get(TEXT("PLAYFAB")));
   ```

3. **繫結至 `AuthenticateUserComplete` 委派**

   ```cpp theme={null}
   const FOnlineIdentityPlayFabPtr PlayFabIdentityInterface = PFOnlineSub->GetIdentityInterfacePlayFab();
   PlayFabIdentityInterface->AddOnAuthenticateUserCompleteDelegate_Handle
   (
       0, // LocalUserNum
       FOnAuthenticateUserCompleteDelegate::CreateUObject(this, &UGameInstance::OnAuthenticateUserComplete)
   );
   ```

   繫結至此委派的函式必須具有與委派相同的簽章。例如：

   ```cpp theme={null}
   void UGameInstance::OnAuthenticateUserComplete(int32 localUserNum, bool bWasSuccessful, const FString& platformUserIdStr, const FString& error)
   ```

   您也可以將 Lambda 繫結至委派。例如：

   ```cpp theme={null}
   // We don't pass the `FOnlineIdentityPlayFabPtr` pointer through the lambda capture since the original
   // local isn't guaranteed to live longer than the lambda and we want to avoid dangling references.
   //
   // You can retrieve `FOnlineIdentityPlayFabPtr` again inside the lambda.
   PlayFabIdentityInterface->AddOnAuthenticateUserCompleteDelegate_Handle
   (
       0, // LocalUserNum
       FOnAuthenticateUserCompleteDelegate::CreateLambda([this](int32 localUserNum, bool bWasSuccessful, const FString& platformUserIdStr, const FString& error)
       {
           // lambda execution
       });
   )
   ```

4. **處理驗證結果**：在繫結至委派的函式中，處理驗證的結果。如果驗證成功，請透過委派觸發所提供的 `platformUserIdStr` 擷取已驗證的本機使用者。接著，從 PlayFab OSS `LocalUser` 建構 PlayFab `LoginResult`。
   ```cpp theme={null}
   void UGameInstance::OnAuthenticateUserComplete(int32 localUserNum, bool bWasSuccessful, const FString& platformUserIdStr, const FString& error)
   {
       if (!bWasSuccessful)
       {
           UE_LOG(LogOnlineGame, Error, TEXT("OSS PlayFab AuthenticateLocalUser Failed. Error: %s"), *error);
           return;
       }

       UE_LOG(LogOnlineGame, Display, TEXT("[%s] AuthenticateLocalUser Complete"), ANSI_TO_TCHAR(__FUNCTION__));

       FOnlineSubsystemPlayFab* PFOnlineSub = static_cast<FOnlineSubsystemPlayFab*>(IOnlineSubsystem::Get(TEXT("PLAYFAB")));
       const FOnlineIdentityPlayFabPtr PlayFabIdentityInterface = PFOnlineSub->GetIdentityInterfacePlayFab();

       if (PlayFabIdentityInterface)
       {
           if (const auto LocalUser = PlayFabIdentityInterface->GetPartyLocalUserFromPlatformIdString(platformUserIdStr))
           {
               // Setting PlayFab SDK Settings for future API calls
               auto& PFCommonModule = IPlayFabCommonModuleInterface::Get();
               PFCommonModule.SetClientSessionTicket(LocalUser->GetSessionTicket());
               PFCommonModule.SetEntityToken(LocalUser->GetEntityToken());

               // Create EntityKey
               auto LocalEntityKey = MakeShared<PlayFab::ClientModels::FEntityKey>();
               LocalEntityKey->Id = LocalUser->GetEntityId();
               LocalEntityKey->Type = LocalUser->GetEntityType();

               // Create EntityToken
               auto LocalEntityToken = MakeShared<PlayFab::ClientModels::FEntityTokenResponse>();
               LocalEntityToken->Entity = LocalEntityKey;
               LocalEntityToken->EntityToken = LocalUser->GetEntityToken();

               // Create LoginResult
               PlayFab::ClientModels::FLoginResult LoginResult;
               LoginResult.EntityToken = LocalEntityToken;
               LoginResult.LastLoginTime = LocalUser->GetEntityTokenUpdateTime();
               LoginResult.PlayFabId = LocalUser->GetPlayFabId();
               LoginResult.SessionTicket = LocalUser->GetSessionTicket();

               // With `LoginResult` populated, we can now use the PlayFab SDK Plugin APIs
               // however we wish. This state is considered "authenticated".
               //
               // This delegate signature functions does not return anything. You can pass `LoginResult`
               // out of this function however you wish.
               // 
               // Example psuedocode for a non-existent function that takes `LoginResult` as a parameter:
               // void UGameInstance::OnAPILoginSuccess(PlayFab::ClientModels::FLoginResult LoginResult);
           }
       }
   }
   ```

5. **使用 PlayFab SDK 外掛程式 API**：填入 `LoginResult` 之後，您現在就可以隨意使用 PlayFab SDK 外掛程式 API。此狀態被視為「已驗證」。

<a id="full-code-example" />

## 完整程式碼範例

```cpp theme={null}
#ifdef WITH_OSS_PLAYFAB
#include "OnlineSubsystemPlayFab.h"
#endif // WITH_OSS_PLAYFAB

#ifdef WITH_PLAYFAB_PLUGIN
#include "PlayFab.h"
#include "PlayFabCommon.h"
#include "Core/PlayFabClientDataModels.h"
#endif // WITH_PLAYFAB_PLUGIN

...

void UGameInstance::Init()
{
    Super::Init();
    ...

    // This is an example scenario of integrating the PlayFab OnlineSubsystem with the
    // PlayFab SDK Plugin.
    //
    // In this scenario, you can authenticate via the PlayFab OSS,
    // and then re-use that authentication with the PlayFab SDK Plugin.
    // 
    // PF OSS offers a public `AuthenticateUserComplete` delegate that can be bound;
    // this delegate triggers on completion of the PF OSS user authentication flow.
    // 
    // Below is an example of a game binding to that delegate, retrieving the
    // authenticated local user via the `platformUserIdStr` from the delegate
    // trigger, and then constructing a PlayFab `LoginResult` from the PF OSS
    // `LocalUser.`
    // 
    // These definitions are defined in `<game>.build.cs`.
#if defined(WITH_OSS_PLAYFAB) && defined(WITH_PLAYFAB_PLUGIN)
    // Explicitly retrieve the PlayFab OSS - we can't assume it's the default OSS.
    FOnlineSubsystemPlayFab* PFOnlineSub = static_cast<FOnlineSubsystemPlayFab*>(IOnlineSubsystem::Get(TEXT("PLAYFAB")));

    UE_LOG(LogOnlineGame, Warning, TEXT("Retrieved PFOnlineSub"));

    if (PFOnlineSub)
    {
        const FOnlineIdentityPlayFabPtr PlayFabIdentityInterface = PFOnlineSub->GetIdentityInterfacePlayFab();

        PlayFabIdentityInterface->AddOnAuthenticateUserCompleteDelegate_Handle
        (
            0, // LocalUserNum
            FOnAuthenticateUserCompleteDelegate::CreateUObject(this, &UGameInstance::OnAuthenticateUserComplete)
        );
    }
    else
    {
        UE_LOG(LogOnlineGame, Error, TEXT("UGameInstance::Init: Unable to find OnlineSubsystemPlayFab"));
    }
#endif // WITH_OSS_PLAYFAB && WITH_PLAYFAB_PLUGIN

    ...
}

void UGameInstance::OnAuthenticateUserComplete(int32 localUserNum, bool bWasSuccessful, const FString& platformUserIdStr, const FString& error)
{
    if (!bWasSuccessful)
    {
        UE_LOG(LogOnlineGame, Error, TEXT("OSS PlayFab AuthenticateLocalUser Failed. Error: %s"), *error);
        return;
    }

    UE_LOG(LogOnlineGame, Display, TEXT("[%s] AuthenticateLocalUser Complete"), ANSI_TO_TCHAR(__FUNCTION__));

    FOnlineSubsystemPlayFab* PFOnlineSub = static_cast<FOnlineSubsystemPlayFab*>(IOnlineSubsystem::Get(TEXT("PLAYFAB")));
    const FOnlineIdentityPlayFabPtr PlayFabIdentityInterface = PFOnlineSub->GetIdentityInterfacePlayFab();

    if (PlayFabIdentityInterface)
    {
        if (const auto LocalUser = PlayFabIdentityInterface->GetPartyLocalUserFromPlatformIdString(platformUserIdStr))
        {
            // Setting PlayFab SDK Settings for future API calls
            auto& PFCommonModule = IPlayFabCommonModuleInterface::Get();
            PFCommonModule.SetClientSessionTicket(LocalUser->GetSessionTicket());
            PFCommonModule.SetEntityToken(LocalUser->GetEntityToken());

            // Create EntityKey
            auto LocalEntityKey = MakeShared<PlayFab::ClientModels::FEntityKey>();
            LocalEntityKey->Id = LocalUser->GetEntityId();
            LocalEntityKey->Type = LocalUser->GetEntityType();

            // Create EntityToken
            auto LocalEntityToken = MakeShared<PlayFab::ClientModels::FEntityTokenResponse>();
            LocalEntityToken->Entity = LocalEntityKey;
            LocalEntityToken->EntityToken = LocalUser->GetEntityToken();

            // Create LoginResult
            PlayFab::ClientModels::FLoginResult LoginResult;
            LoginResult.EntityToken = LocalEntityToken;
            LoginResult.LastLoginTime = LocalUser->GetEntityTokenUpdateTime();
            LoginResult.PlayFabId = LocalUser->GetPlayFabId();
            LoginResult.SessionTicket = LocalUser->GetSessionTicket();

            // With `LoginResult` populated, we can now use the PlayFab SDK Plugin APIs
            // however we wish. This state is considered "authenticated".
            //
            // This delegate signature functions does not return anything. You can pass `LoginResult`
            // out of this function however you wish.
            // 
            // Example psuedocode for a non-existent function that takes `LoginResult` as a parameter:
            // void UGameInstance::OnAPILoginSuccess(PlayFab::ClientModels::FLoginResult LoginResult);
        }
    }
}
```


## Related topics

- [PlayFab Unreal GSDK 外掛程式概觀](/zh-TW/services/playfab/multiplayer/servers/server-sdks/unreal-gsdk/index.md)
- [Unreal Engine 快速入門](/zh-TW/services/playfab/sdks/unreal/quickstart.md)
- [PlayFab for Unreal Engine 概觀](/zh-TW/services/playfab/sdks/unified-unreal/overview.md)
- [PlayFab Multiplayer Unity 外掛程式概觀](/zh-TW/services/playfab/multiplayer/lobby/lobby-matchmaking-sdks/multiplayer-unity-overview.md)
- [適用於 Unreal Engine 的 PlayFab OnlineSubsystem 版本資訊](/zh-TW/services/playfab/multiplayer/networking/unreal-release-notes.md)
