다음을 통해 공유


여러 PlayFab 로그인 처리

기본적으로 대부분의 PlayFab SDK는 플레이어 로그인 결과를 캐시합니다. 이 캐싱은 단일 플레이어 로그인이 예상되는 가장 일반적인 시나리오에서 편리하게 작용할 수 있습니다. 여러 동시 플레이어를 지원하는 게임에서 또는 플레이어의 인증 수단과 서버 자격 증명을 모두 관리하는 서버에서는 이러한 캐싱이 방해가 될 수 있습니다. 이러한 시나리오에서의 용이성을 위해 PlayFab SDK에는 정적 API 클래스 및 인스턴스화된 API 클래스가 모두 포함되어 있습니다.

정적 API 클래스 및 인스턴스화된 API 클래스 비교

대부분의 PlayFab용 예제 및 샘플 코드는 정적 API 클래스를 사용하여 빌드됩니다. 예를 들어 Unity에서는 PlayFabClientAPI에 대한 참조를 볼 수 있습니다. 이러한 정적 클래스는 SDK 내의 정적 상태에서 쓰고 읽습니다. 정적 클래스는 정적 상태에 대한 종속성으로 인해 동일한 게임 클라이언트 내에서 여러 플레이어를 처리할 때는 사용하기가 어렵습니다. 인스턴스화된 API 클래스를 사용하면 관리 및 추적 요건이 늘어나는 대신 이러한 문제를 방지할 수 있습니다. 여러 동시 PlayFab 로그인을 지원해야 하는 경우 인스턴스화된 API 클래스를 사용하는 것이 좋습니다.

Unity SDK에서 PlayFabClientInstanceAPIPlayFabClientAPI의 인스턴스화된 버전입니다. 다른 모든 API 클래스는 비슷한 명명 패턴을 따릅니다. API 클래스의 인스턴스화된 버전을 사용하는 경우 메서드를 호출하려면 먼저 클래스의 인스턴스를 만들어야 합니다. 인스턴스를 만들려면 추가 컨텍스트를 지정해야 합니다. 대부분의 클래스에서 필요한 추가 컨텍스트는 플레이어의 인증 컨텍스트뿐입니다. Unity에서 이 컨텍스트는 PlayFabAuthenticationContext입니다. 일부 클래스에는 로그인 호출이 포함됩니다. 이러한 호출의 경우 플레이어의 인증 컨텍스트가 아직 없을 수 있습니다. 이 경우 타이틀 ID와 같은 기본 설정만 제공해야 합니다. 이러한 설정은 PlayFabApiSettings 개체를 통해 전달됩니다.

인스턴스화된 API 클래스 사용

적절한 인증 컨텍스트 또는 API 설정 개체를 사용하여 인스턴스화된 API 클래스를 만든 후에는 정적 클래스와 유사하게 사용할 수 있습니다. 모든 요청 및 응답 개체가 동일합니다. 유일한 차이점은 인스턴스의 수명을 추적해야 하며 PlayFab의 잠재적 호출자에 적절한 인스턴스가 제공되는지 확인해야 한다는 점입니다. 여러 플레이어를 처리할 때는 API 클래스 인스턴스가 플레이어당 하나씩, 여러 개 필요합니다. 플레이어 개체 뒤에 캡슐화된 이러한 인스턴스를 소유자로서 관리하는 것이 대체로 가장 간편하지만, 실제로 그렇게 할지는 직접 선택할 수 있습니다.

플레이어에 로그인하는 인스턴스화된 클래스(예: PlayFabClientInstanceAPI)도 API 클래스 인스턴스 내에서 해당 플레이어에 대한 인증 컨텍스트를 만들고 캐시합니다. 이 기능을 통해 필요한 클래스를 더 만들 때 인증 컨텍스트를 쉽게 참조할 수 있습니다.

Unity 예제

이 샘플 코드는 여러 플레이어가 게임에 로그인하고 별도의 클래스 인스턴스에서 독립적인 상태가 추적되도록 하는 방법을 보여줍니다. 이 예제에서 API 클래스 및 기본 기능은 간단한 PlayFabPlayer 개체 뒤에 캡슐화됩니다. 게임이 시작되면 두 명의 플레이어에 로그인한 다음 각 플레이어에 대해 PlayFab에 저장된 모든 데이터를 가져옵니다.

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 예제

이 코드 샘플에서는 자체 PlayFab 로그인 컨텍스트를 포함하는 Unreal의 actor를 보여 줍니다. ALoginActor 클래스 내에서 PlayFab API 인스턴스 클래스를 캡슐화하는 방법을 보여 줍니다. 여러 ALoginActor 인스턴스를 맵에 추가하고, 자체 CustomId를 지정하고, PlayFab 작업을 독립적으로 수행할 수 있습니다.

LoginActor.h:

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

#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 클래스를 통해 게임 클라이언트가 여러 플레이어를 처리할 수 있는 것과 유사하게 서버가 타이틀과 플레이어 인증을 함께 처리하거나 여러 타이틀을 동시에 처리할 수도 있습니다. 기본 패턴은 거의 동일합니다. API 클래스를 인스턴스화하여 서버 로그인을 처리하고, 해당 인스턴스에 적절한 PlayFabApiSettings 개체를 제공한 다음, 인증 API를 호출합니다. 서버에 있는 경우 해당 API는 일반적으로 PlayFabAuthenticationInstanceAPI.GetEntityToken입니다. 플레이어 로그인과 마찬가지로 GetEntityToken 호출의 결과는 API 클래스 인스턴스에 캐시되며 authenticationContext 인스턴스 속성을 통해 더 많은 API 클래스 인스턴스를 만드는 데 참조될 수 있습니다.