> ## 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 登录

> 使用 PlayFab SDK 实例化 API 类从单个客户端或服务器管理多个并发玩家登录，而不会有冲突的静态状态。

默认情况下，大多数 PlayFab SDK 会缓存玩家登录结果。当你期望单个玩家登录时，这种缓存在最常见的场景中很方便。在你支持多个并发玩家的游戏或你管理玩家身份验证材料和服务器凭证的服务器中，这种缓存可能会造成阻碍。为方便这些场景，PlayFab SDK 包含静态和实例化两种 API 类。

## 静态与实例化 API 类

PlayFab 的大多数示例和示例代码都是使用静态 API 类构建的。例如，在 Unity 中，你可能会看到对 **PlayFabClientAPI** 的引用。这些静态类写入和读取 SDK 内的静态状态。它们对静态状态的依赖使得在同一游戏客户端中处理多个玩家时难以使用这些类。实例化 API 类避免了这些问题，以增加你的管理和跟踪要求为代价。当你需要支持多个同时进行的 PlayFab 登录时，我们建议使用实例化 API 类。

在 Unity SDK 中，**PlayFabClientInstanceAPI** 是 **PlayFabClientAPI** 的实例化版本。所有其他 API 类都遵循类似的命名模式。当使用 API 类的实例化版本时，你必须先创建该类的实例，然后才能调用任何方法。要创建实例，你必须指定一些额外的上下文。对于大多数类，所需的额外上下文只是玩家的身份验证上下文。在 Unity 中，此上下文是 **PlayFabAuthenticationContext**。有些类包含登录调用。对于这些调用，你可能还没有玩家的身份验证上下文。如果是这种情况，你只需提供基本设置，如 title ID。这些设置通过 **PlayFabApiSettings** 对象传入。

## 使用实例化 API 类

一旦你使用适当的身份验证上下文或 API 设置对象创建了实例化 API 类，就可以像使用静态类那样使用它。所有请求和响应对象都相同。唯一的区别是你负责跟踪实例的生命周期，并确保任何 PlayFab 的潜在调用方都能获得适当的实例。当你处理多个玩家时，你需要多个 API 类实例，每个玩家一个。通常最简单的做法是将这些实例封装在作为所有者的 player 对象后面进行管理，但这由你决定。

登录玩家的实例化类（例如 **PlayFabClientInstanceAPI**）也会在该 API 类实例中创建并缓存该玩家的身份验证上下文。此功能允许你在创建任何更多所需的类时轻松引用身份验证上下文。

## Unity 示例

此示例代码演示了多个玩家如何登录游戏，并在单独的类实例中跟踪独立的状态。在此示例中，API 类和基本功能封装在一个简单的 **PlayFabPlayer** 对象后面。当游戏开始时，我们让两个玩家登录，然后获取 PlayFab 中为每个玩家存储的任何数据。

```csharp theme={null}
using PlayFab;
using PlayFab.ClientModels;
using PlayFab.DataModels;
using System.Collections.Generic;
using UnityEngine;

public class PlayFabLogin : MonoBehaviour
{
    PlayFabPlayer player1 = new PlayFabPlayer();
    PlayFabPlayer player2 = new PlayFabPlayer();

    // Start is called before the first frame update
    void Start()
    {
        if (string.IsNullOrEmpty(PlayFabSettings.staticSettings.TitleId))
        {
            // Please change the titleId below to your own titleId from PlayFab Game Manager.
            PlayFabSettings.staticSettings.TitleId = "";
        }

        player1.Login("testLogin1");
        player2.Login("testLogin2");
    }

    // Update is called once per frame
    void Update()
    {
        if (player1.loggedIn && !player1.dataLoaded && !player1.dataLoading)
        {
            player1.LoadData();
        }
        if (player2.loggedIn && !player2.dataLoaded && !player2.dataLoading)
        {
            player2.LoadData();
        }
    }
}

class PlayFabPlayer
{
    public bool loggedIn = false;
    public bool dataLoading = false;
    public bool dataLoaded = false;
    public string PlayFabId;
    public Dictionary<string, ObjectResult> playerData;

    private PlayFabClientInstanceAPI clientApi;
    private PlayFabDataInstanceAPI dataApi;

    public void Login(string customId)
    {
        clientApi = new PlayFabClientInstanceAPI(PlayFabSettings.staticSettings);

        var request = new LoginWithCustomIDRequest { CustomId = customId, CreateAccount = true };

        clientApi.LoginWithCustomID(request, result =>
        {
            PlayFabId = result.PlayFabId;
            loggedIn = true;
            dataApi = new PlayFabDataInstanceAPI(clientApi.authenticationContext);
            Debug.Log("Login call succeeded.");
        }, error =>
        {
            Debug.LogWarning("Something went wrong with the login call.");
            Debug.LogError("Here's some debug information:");
            Debug.LogError(error.GenerateErrorReport());
        });
    }

    public void LoadData()
    {
        dataLoading = true;
        var request = new GetObjectsRequest { Entity = new PlayFab.DataModels.EntityKey { Id = clientApi.authenticationContext.EntityId, Type = clientApi.authenticationContext.EntityType } };

        dataApi.GetObjects(request, result =>
        {
            playerData = result.Objects;
            dataLoaded = true;
            dataLoading = false;
            Debug.Log("Player data loaded.");
        }, error =>
        {
            Debug.LogWarning("Something went wrong with the GetObjects call.");
            Debug.LogError("Here's some debug information:");
            Debug.LogError(error.GenerateErrorReport());
        });
    }
}
```

## Unreal 示例

此代码示例演示了 Unreal 中一个包含其自身 PlayFab 登录上下文的 actor。它展示了如何将 PlayFab API 实例类封装在 **ALoginActor** 类中。可以将多个 **ALoginActor** 实例添加到地图，赋予它们自己的 CustomId，并独立执行 PlayFab 操作。

LoginActor.h：

```cpp theme={null}
#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "PlayFab.h"
#include "Core/PlayFabError.h"
#include "Core/PlayFabClientDataModels.h"
#include "Core/PlayFabClientAPI.h"
#include "Core/PlayFabDataAPI.h"
#include "LoginActor.generated.h"

UCLASS()
class MINUE_PF_MARKET_API ALoginActor : public AActor
{
	GENERATED_BODY()
	
public:	
	// Sets default values for this actor's properties
	ALoginActor();

    // Please change the TitleId below to your own TitleId from PlayFab Game Manager.
	UPROPERTY(EditAnywhere, config, Category = Settings)
	FString TitleId = TEXT("");

	UPROPERTY(EditAnywhere, config, Category = Settings)
	FString CustomId = TEXT("ExampleCustomId");

protected:
	// Called when the game starts or when spawned

	virtual void BeginPlay() override;

	void OnLoginSuccess(const PlayFab::ClientModels::FLoginResult& Result);
	void OnGetObjectsSuccess(const PlayFab::DataModels::FGetObjectsResponse& Result);
	void OnError(const PlayFab::FPlayFabCppError& ErrorResult) const;

public:	
	bool LoggedIn = false;

	// Called every frame
	virtual void Tick(float DeltaTime) override;

	PlayFabClientPtr clientAPI = nullptr;
	PlayFabDataPtr dataAPI = nullptr;

	TMap<FString, PlayFab::DataModels::FObjectResult> PlayerData;
	bool DataLoaded = false;
};
```

LoginActor.cpp：

```cpp theme={null}
#include "LoginActor.h"

ALoginActor::ALoginActor()
{
 	// Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;
}

void ALoginActor::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);
}

void ALoginActor::BeginPlay()
{
	Super::BeginPlay();
    GetMutableDefault<UPlayFabRuntimeSettings>()->TitleId = TitleId;

    clientAPI = IPlayFabModuleInterface::Get().GetClientAPI();
    dataAPI = IPlayFabModuleInterface::Get().GetDataAPI();

    PlayFab::ClientModels::FLoginWithCustomIDRequest request;
    request.CustomId = CustomId;
    request.CreateAccount = true; 

    clientAPI->LoginWithCustomID(request,
        PlayFab::UPlayFabClientAPI::FLoginWithCustomIDDelegate::CreateUObject(this, &ALoginActor::OnLoginSuccess),
        PlayFab::FPlayFabErrorDelegate::CreateUObject(this, &ALoginActor::OnError)
    );
}

void ALoginActor::OnLoginSuccess(const PlayFab::ClientModels::FLoginResult& Result)
{
    UE_LOG(LogTemp, Log, TEXT("Login call succeeded."));
    LoggedIn = true;

    PlayFab::DataModels::FGetObjectsRequest dataRequest;
    dataRequest.AuthenticationContext = Result.AuthenticationContext;
    dataRequest.Entity.Id = Result.EntityToken->Entity->Id;
    dataRequest.Entity.Type = Result.EntityToken->Entity->Type;

    dataAPI->GetObjects(
        dataRequest,
        PlayFab::UPlayFabDataAPI::FGetObjectsDelegate::CreateUObject(this, &ALoginActor::OnGetObjectsSuccess),
        PlayFab::FPlayFabErrorDelegate::CreateUObject(this, &ALoginActor::OnError)
    );
}

void ALoginActor::OnError(const PlayFab::FPlayFabCppError& ErrorResult) const
{
    UE_LOG(LogTemp, Error, TEXT("Something went wrong with your API call.\nHere's some debug information:\n%s"), *ErrorResult.GenerateErrorReport());
}

void ALoginActor::OnGetObjectsSuccess(const PlayFab::DataModels::FGetObjectsResponse& Result)
{
    PlayerData = Result.Objects;
    DataLoaded = true;
	UE_LOG(LogTemp, Log, TEXT("Player data loaded."));    
}
```

## 服务器身份验证

类似于实例化 API 类允许游戏客户端处理多个玩家，它们允许服务器处理 title 和玩家身份验证的组合，甚至同时处理多个 title。基本模式几乎相同。实例化一个 API 类以处理服务器登录，为该实例提供适当的 **PlayFabApiSettings** 对象，然后调用身份验证 API。如果你在服务器上，该 API 通常是 **PlayFabAuthenticationInstanceAPI.GetEntityToken**。就像玩家登录一样，**GetEntityToken** 调用的结果被缓存在 API 类实例中，并且可以通过 **authenticationContext** 实例属性引用以创建更多 API 类实例。


## Related topics

- [在 Unity 中使用 Google Play Games 登录进行 PlayFab 身份验证](/zh-CN/services/playfab/identity/player-identity/platform-specific-authentication/google-sign-in-unity.md)
- [多人游戏会话高级主题](/zh-CN/services/xbox-services/multiplayer/mpsd/concepts/live-mpsd-details.md)
- [从 PlayFab 独立 SDK v1 迁移到统一 SDK v2](/zh-CN/services/playfab/sdks/unified-sdk/migrating-from-v1.md)
- [库存堆栈](/zh-CN/services/playfab/economy-monetization/economy-v2/inventory/stacks.md)
- [包（Packages）](/zh-CN/publishing/game-publishing/concepts/game-package-management.md)
