> ## 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.

# Control de varios inicios de sesión de PlayFab

> Use las clases de API con instancias del SDK de PlayFab para administrar varios inicios de sesión de jugador simultáneos desde un único cliente o servidor sin conflictos de estado estático.

De forma predeterminada, la mayoría de los SDK de PlayFab almacenan en caché los resultados de inicio de sesión del jugador. Este almacenamiento en caché puede resultar cómodo en el escenario más común, cuando espera un único inicio de sesión de jugador. En los juegos donde se admiten varios jugadores simultáneos o en servidores donde se administra tanto el material de autenticación de los jugadores como las credenciales del servidor, ese almacenamiento en caché puede ser un obstáculo. Para facilitar estos escenarios, los SDK de PlayFab contienen tanto clases de API estáticas como clases de API con instancias.

## Clases de API estáticas frente a clases de API con instancias

La mayoría de los ejemplos y del código de muestra de PlayFab se crean con clases de API estáticas. En Unity, por ejemplo, es posible que vea una referencia a **PlayFabClientAPI**. Estas clases estáticas escriben y leen el estado estático dentro del SDK. Su dependencia del estado estático hace difícil usar estas clases cuando se trabaja con varios jugadores dentro del mismo cliente de juego. Las clases de API con instancias evitan estos problemas a cambio de mayores requisitos de administración y seguimiento por su parte. En los casos en los que necesite admitir varios inicios de sesión de PlayFab simultáneos, se recomienda usar clases de API con instancias.

En el SDK de Unity, **PlayFabClientInstanceAPI** es la versión con instancias de **PlayFabClientAPI**. Todas las demás clases de API siguen un patrón de nomenclatura similar. Cuando use la versión con instancias de una clase de API, debe crear una instancia de la clase antes de poder llamar a cualquier método. Para crear una instancia, tiene que especificar algún contexto adicional. Para la mayoría de las clases, el contexto adicional necesario es simplemente el contexto de autenticación del jugador. En Unity, este contexto es **PlayFabAuthenticationContext**. Algunas clases contienen llamadas de inicio de sesión. Para estas llamadas, es posible que aún no tenga el contexto de autenticación de un jugador. Si es así, solo tiene que proporcionar una configuración básica, como un id. de título. Esta configuración se pasa mediante un objeto **PlayFabApiSettings**.

## Uso de clases de API con instancias

Una vez que haya creado una clase de API con instancias con un contexto de autenticación o un objeto de configuración de API apropiados, se puede usar de forma similar a una clase estática. Todos los objetos de solicitud y respuesta son idénticos. La única diferencia es que usted es responsable de hacer el seguimiento de la duración de la instancia y de asegurarse de que cualquier posible llamador de PlayFab reciba una instancia apropiada. Cuando trabaje con varios jugadores, necesitará varias instancias de cualquier clase de API, una por jugador. A menudo, lo más fácil es administrar estas instancias encapsuladas detrás de un objeto de jugador como propietario, pero eso queda a su elección.

Las clases con instancias que inician la sesión de un jugador (como **PlayFabClientInstanceAPI**) también crean y almacenan en caché el contexto de autenticación de ese jugador dentro de la instancia de la clase de API. Esta característica le permite hacer referencia fácilmente al contexto de autenticación al crear cualquier otra clase que necesite.

## Ejemplo de Unity

Este código de ejemplo muestra cómo varios jugadores pueden iniciar sesión en un juego y tener un estado independiente del que se hace el seguimiento en instancias de clase separadas. En este ejemplo, las clases de API y la funcionalidad básica se encapsulan detrás de un objeto **PlayFabPlayer** simplista. Cuando se inicia el juego, iniciamos la sesión de dos jugadores y, después, obtenemos los datos almacenados en PlayFab para cada uno de ellos.

```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());
        });
    }
}
```

## Ejemplo de Unreal

Este código de ejemplo muestra un actor de Unreal que contiene su propio contexto de inicio de sesión de PlayFab. Muestra cómo encapsular las clases de instancia de la API de PlayFab dentro de una clase **ALoginActor**. Se pueden agregar varias instancias de **ALoginActor** a un mapa, asignarles sus propios CustomIds y realizar operaciones de PlayFab de forma independiente.

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."));    
}
```

## Autenticación de servidor

De forma similar a como las clases de API con instancias permiten que un cliente de juego controle varios jugadores, también permiten que los servidores controlen una combinación de autenticación de título y de jugador, o incluso varios títulos simultáneamente. El patrón básico es casi idéntico. Cree una instancia de una clase de API para controlar el inicio de sesión del servidor, proporcione a esa instancia el objeto **PlayFabApiSettings** apropiado y, después, llame a una API de autenticación. Si está en un servidor, esa API suele ser **PlayFabAuthenticationInstanceAPI.GetEntityToken**. Al igual que con el inicio de sesión de un jugador, el resultado de una llamada a **GetEntityToken** se almacena en caché en la instancia de la clase de API y se puede usar como referencia para crear más instancias de clases de API mediante la propiedad de instancia **authenticationContext**.
