Unreal: Parties #

This tutorial uses Unreal Engine 5.3 with Pragma Engine 0.1.0 to demonstrate integrating Pragma Engine party functionality with a third-party game engine. This guide assumes you are proficient with Unreal Editor.

After creating a Party Plugin in Pragma Engine, you can build the corresponding frontend in Unreal Engine with help from the Pragma Party API. In this tutorial, we’ll expand the MyPlayerController.h header file and MyPlayerController.cpp source file we created in the Handle Login and Logout Unreal tutorial to implement party functionality. By the end of the tutorial, our game client will be able to create, join, and leave parties, as well as make game mode and character selections.

Get started #

To get started, re-run the update script command from your Unreal Plugins folder:

update-pragma-sdk.sh

Ensure you have a locally running Pragma Engine to test examples as you build each function.

How to use this tutorial #

The code presented in this tutorial is simplified to give you an introduction to the game flow. An actual game would have a more sophisticated design, and the player experience may differ significantly.

We’ve built this barebones party screen to help you visualize the functionality presented in this tutorial:

The functions in this tutorial are built as UFunctions with the Exec and BlueprintCallable specifiers, meaning they can be executed by the in-game console and in a Blueprint or Level Blueprint graph. The Exec specifier is useful for quickly testing your functions.

The example tests in each section are meant to ensure your C++ code is working properly and are unlikely to represent a completed game design. Adapt the organization and naming to suit your project’s needs.

For convenience, we’ve included sample C++ files that contain all the code from this tutorial as well as the login/logout functionality.

Note that you may need to update your #include statements as you progress through the tutorial.

Implement the Initialize Party function (required) #

Goal #

Implement a InitializeParty() function that initializes the PartyApi. In order to use the Pragma Party API functions, each player needs to to call PartyApi().Initialize. You can also choose to implement the following functionality into a general Initialize() function that also initializes the Game Instance, Friend, and Presence services (see the relevant tutorials for examples).

Steps #

  1. Declare the InitializeParty() function in the public section of your MyPlayerController.h file:

    UFUNCTION(Exec, BlueprintCallable, meta=(Category="Pragma"))
    void InitializeParty();
    
  2. Define InitializeParty() in your MyPlayerController.cpp file:

    void AMyPlayerController::InitializeParty()
    {
    	UPragmaPartyApi::FOnCompleteDelegate PartyInitializeDelegate;
    	PartyInitializeDelegate.BindWeakLambda(this, [this](const TPragmaResult<>& Result)
    		{
    			if (Result.IsSuccessful())
    			{
    				UE_LOG(LogTemp, Display, TEXT("Party service initialized."));
    			}
    			else
    			{
    				UE_LOG(LogTemp, Warning, TEXT("Party service failed to initialize: %s"), *Result.Error().ToString());
    			}
    		});
    	Player->PartyApi().Initialize(PartyInitializeDelegate);
    }
    

Test #

To test this functionality using the Unreal in-game console, as a logged in user, call InitializeParty.

To apply this functionality using Unreal Blueprints, you can call the InitializeParty() function as part of another operation, such as upon login.

When the service is successfully initialized, you should see “Party service initialized.” in the Unreal output log.

Create the party #

Goal #

Implement a CreateParty() function to allow a player to create a party from Unreal.

Upon party creation, parties are automatically assigned a unique party ID and invite code. In our example, we have the CreateParty() function return the party ID and invite code for easy access.

Steps #

  1. Declare the CreateParty() function in the public section of your MyPlayerController.h file:

    UFUNCTION(Exec, BlueprintCallable, Category="Pragma")
    void CreateParty();
    
  2. Define CreateParty() in the MyPlayerController.cpp file to create the party and add the current player as the party leader:

    void AMyPlayerController::CreateParty()
    {
    	UPragmaPartyApi::FOnCompleteDelegate OnPartyCreatedDelegate;
    	OnPartyCreatedDelegate.BindWeakLambda(this, [this](TPragmaResult<> Result)
    	{
    		if (Result.IsSuccessful())
    		{
    			UE_LOG(LogTemp, Display, TEXT("Pragma party created.  \n Party ID: %s \n Invite code: %s"),
    				*Player->PartyApi().GetPartyCache()->Party()->GetId(),
    				*Player->PartyApi().GetPartyCache()->Party()->GetInviteCode());
    		}
    		else
    		{
    			UE_LOG(LogTemp, Warning, TEXT("Pragma unable to create party: %s"), *Result.Error().ToString());
    		}
    	});
    
    	Player->PartyApi().CreateParty(
    		FPragma_Party_ExtCreateRequest{},
    		FPragma_Party_ExtPlayerJoinRequest{},
    		TArray<FString>(),
    		TMap<FString, int32>(),
    		OnPartyCreatedDelegate);
    }
    

Our CreateParty() function simply calls the Pragma PartyApi’s CreateParty() function, which handles creating a party on the backend. Our example function does not accept any extra data for party creation, but you can add parameters to your CreateParty() Unreal function and customize the ExtCreateRequest and ExtPlayerJoinRequest protos to allow for party customization during the time of creation.

Our function prints out the party’s invite code and party ID. These are values set on the party and can be distributed to allow others to join the party (see Join party with invite code or party ID).

Test #

To test this functionality using the Unreal in-game console, call CreateParty() as a logged-in user. You should see “Pragma party created with code [Party join code].” in your Unreal output log. Record the party ID and invite code to use later.

To apply this functionality using Unreal Blueprints, create a button that calls your CreateParty() function when clicked. Populate text boxes with the returned party ID and party invite code values.

Join party with invite code or party ID #

Goal #

Define the functions that will allow a player to join a party using the party’s public invite code or party ID.

Upon party creation, parties are automatically assigned a unique party ID and invite code. To access these values, you can have the CreateParty() function return the party invite code and store it locally, or access the code with a call to Player->PartyApi().GetPartyCache()->Party()->GetInviteCode().

Steps #

  1. Declare the JoinPartyWithInviteCode() and JoinPartyWithPartyId() functions in the public section of the MyPlayerController.h file:

    UFUNCTION(Exec, BlueprintCallable, Category="Pragma")
    void JoinPartyWithInviteCode(const FString& InviteCode);
    
    UFUNCTION(Exec, BlueprintCallable, Category="Pragma")
    void JoinPartyWithPartyId(const FString& PartyId);
    
  2. Define JoinPartyWithInviteCode() and JoinPartyWithPartyId() in the MyPlayerController.cpp:

    void AMyPlayerController::JoinPartyWithInviteCode(const FString& InviteCode)
    {
    	UPragmaPartyApi::FOnCompleteDelegate JoinWithInviteCodeDelegate;
    	JoinWithInviteCodeDelegate.BindWeakLambda(this, [=, this](TPragmaResult<> Result)
    	{
    		if (Result.IsSuccessful())
    		{
    			UE_LOG(LogTemp, Display, TEXT("Joined party using invite code %s"), *InviteCode);
    		}
    		else
    		{
    			UE_LOG(LogTemp, Warning,
    				TEXT("Unable to join party: %s"), *Result.Error().ToString());
    		}
    	});
    
    	Player->PartyApi().JoinPartyWithInviteCode(
    		FPragma_Party_ExtPlayerJoinRequest{},
    		InviteCode,
    		JoinWithInviteCodeDelegate
    	);
    }
    
    void AMyPlayerController::JoinPartyWithPartyId(const FString& PartyId)
    {
    	UPragmaPartyApi::FOnCompleteDelegate JoinWithPartyIdDelegate;
    	JoinWithPartyIdDelegate.BindWeakLambda(this, [=, this](TPragmaResult<> Result)
    	{
    		if (Result.IsSuccessful())
    		{
    			UE_LOG(LogTemp, Display, TEXT("Joined party using party ID %s"), *PartyId);
    		}
    		else
    		{
    			UE_LOG(LogTemp, Warning,
    				TEXT("Unable to join party: %s"), *Result.Error().ToString());
    		}
    	});
    
    	Player->PartyApi().JoinPartyWithId(
    		FPragma_Party_ExtPlayerJoinRequest{},
    		PartyId,
    		JoinWithPartyIdDelegate
    	);
    }
    

Test #

To test this functionality using the Unreal in-game console:

  1. Log in on two clients (you can use the accounts test01 and test02).

  2. As test01, create a party and record the invite code and party ID.

  3. As test02, issue JoinPartyWithInviteCode [party invite code] to join test01’s party.

  4. As test03, issue JoinPartyWithPartyId [party ID] to join test01’s party.

To apply this functionality using Unreal Blueprints:

  1. Create editable text boxes where a user can input a party invite code or a party ID.

  2. Create buttons that calls your JoinPartyWithInviteCode() and JoinPartyWithPartyId functions when clicked.

  3. Grab the text from the appropriate input box and plug it into the function.

Upon the second client successfully joining the party, you should see a success message in your output log.

Send party invite #

Goal #

Implement a SendPartyInviteByPlayerId() function that allows a player to send a private party invite to another player using the player’s ID. A player’s ID is available via Player->Id().

Tip: Create a GetPlayerId() function that returns Player()->Id() so you can easily access a player’s ID when testing the tutorial.

Steps #

  1. Declare a SendPartyInviteByPlayerId() function in the public section of the MyPlayerController.h file. Have the function accept a string for the invitee’s player ID.

    UFUNCTION(Exec, BlueprintCallable, Category="Pragma")
    void SendPartyInviteByPlayerId(const FString& InviteeId);
    
  2. Define SendPartyInviteByPlayerId() in the MyPlayerController.pp file:

    void AMyPlayerController::SendPartyInviteByPlayerId(const FString& InviteeId)
    {
    
    	UPragmaPartyApi::FOnInviteSentDelegate SendPartyInviteDelegate;
    	SendPartyInviteDelegate.BindWeakLambda(
    		this, [this, InviteeId](const TPragmaResult<FString>& SendPartyInviteResult)
    	{
    		if (SendPartyInviteResult.IsSuccessful())
    		{
    			UE_LOG(LogTemp, Display, TEXT("Party invite sent to player ID: %s"), *InviteeId);
    		}
    		else
    		{
    			UE_LOG(LogTemp, Warning,
    				TEXT("Unable to send invite to player ID: %s Reason: %s"), *InviteeId, *SendPartyInviteResult.Error().ToString());
    		}
    	});
    
    	Player->PartyApi().SendPartyInvite(
    		InviteeId,
    		SendPartyInviteDelegate
    	);
    }
    

Our SendPartyInviteByPlayerId()) function accepts a string value representing the player ID of the invitee. Using this ID, the function calls the PartyApi’s SendPartyInvite() function which facilitates sending a party invite to the identified player.

Test #

To test this functionality using the Unreal in-game console:

  1. Log in on two clients (you can use the accounts test01 and test02) and initialize the party service.

  2. As test01, create a party.

  3. As test01, issue SendPartyInviteByPlayerId with test02’s player ID (available via Player->Id()).

To apply this functionality using Unreal Blueprints:

  1. Create an editable text box where a user can input a player’s ID.

  2. Create a button that calls your SendPartyInviteByPlayerId() function when clicked.

  3. Grab the text from the input box and plug it into the SendPartyInviteByPlayerId node.

You should see a “Party invite sent to player ID: [player ID]” message in your output log, along with any additional UI updates. See the View Party Invites to learn how to alert a player when they receive an invite.

Handle invite received #

Goal #

Alert a player that they’ve received an invite by listening to the OnPartyInviteReceived event. When a party invite is sent, Pragma Engine fires a OnPartyInviteReceived event with a FPragmaPartyInvite object representing the specific party invite. We can listen to this even to alert a player when they receive a party invite, and provide them with the invite ID, which they will need when responding.

You can also listen on the OnPartyInvitesChanged() event for any changes to a party invite list, being careful not to double handle the events.

Steps #

  1. Declare a HandlePartyInviteReceived() function in the private section of the MyPlayerController.h file:

    void HandleOnPartyInviteReceived(const FPragmaPartyInvite& MyPartyInvite);
    
  2. Define HandleOnPartyInviteReceived() in your MyPlayerController.cpp file. Use the FPragmaPartyInvite object from the event to print out the invite ID for the invitee. This is the value they will use to respond to the invite.

    void AMyPlayerController::HandleOnPartyInviteReceived(const FPragmaPartyInvite& MyPartyInvite)
    {
    	UE_LOG(LogTemp, Display, TEXT("Party invite received. Respond using invite ID: %s"), *MyPartyInvite.GetInviteId());
    }
    
  3. Register the HandleOnPartyInviteReceived() function with the OnPartyInviteReceived event handler in the BeginPlay() section of the MyPlayerController.cpp file:

    Player->PartyApi().OnPartyInviteReceived.AddUObject(
    	this, &AMyPlayerController::HandleOnPartyInviteReceived);
    

Test #

To test this functionality using the Unreal in-game console, as a logged-in player, create a party and send a party invite to another player (see Send Party Invite).

You should see “Party invite received. Respond using invite ID: [party invite ID]” in the output log for the invited player, along with any additional UI updates, such as updating a text box with the latest pending invites.

Accept or decline a party invite #

Goal #

Implement a RespondToPartyInvite() function that allows a player to accept or reject an invite. Players respond to a party invite using the party’s invite ID, which is unique for each sent invite, along with a true/false value to accept/decline the invitation.

Steps #

  1. Allow a player to accept or decline a party invite by declaring a RespondToPartyInvite() function in the public section of the MyPlayerController.h file. This function should accept a party invite ID and a true/false response:

    UFUNCTION(Exec, BlueprintCallable, meta=(Category="Pragma"))
    void RespondToPartyInvite(const FString& PartyInviteId, const bool response);
    
  2. Define RespondToPartyInvite() in the MyPlayerController.cpp file:

    void AMyPlayerController::RespondToPartyInvite(const FString& PartyInviteId, const bool Response)
    {
    	UPragmaPartyApi::FOnCompleteDelegate RespondInviteDelegate;
    	RespondInviteDelegate.BindWeakLambda(this, [=, this](TPragmaResult<> Result)
    	{
    		if (Result.IsSuccessful())
    		{
    			if (Response==true)
    			{
    				UE_LOG(LogTemp, Display, TEXT("Accepted party invite. Party successfully joined."));
    			}
    			else
    			{
    				UE_LOG(LogTemp, Display, TEXT("Declined party invite"));
    			}
    		}
    		else
    		{
    			UE_LOG(LogTemp, Warning,
    				TEXT("Unable to respond: %s"), *Result.Error().ToString());
    		}
    	});
    
    	Player->PartyApi().RespondToPartyInvite(
    		FPragma_Party_ExtPlayerJoinRequest{},
    		PartyInviteId,
    		Response,
    		RespondInviteDelegate
    	);
    }
    

Our RespondToPartyInvite() function uses the provided PartyInviteId string to identify a pending party invite for the current player, and forwards that value along with the true/false response to the Pragma PartyApi’s RespondToPartyInvite() function, which facilitates adding the invitee to the party if the invite is accepted.

Test #

To test this functionality:

  1. As a party invitee, record the party invite ID displayed in the success message of the HandleOnPartyInviteReceived() function (see Handle party invite received).

  2. Issue RespondToPartyInvite() with the party invite ID and the value of true (accept) or false (reject).

You should see the appropriate message in your output log, along with any UI updates. If you accepted the party invite, you can verify you joined the party by selecting a character (see Make character selections).

Make game mode selections #

Goal #

Implement a SetGameModeSelection() function that allows party leaders to select a game mode.

Steps #

  1. Declare SetGameModeSelection() in the public section of the MyPlayerController.h file. Have the function accept a game mode value based on the enum we defined in Define party and player options:

    UFUNCTION(Exec, BlueprintCallable, Category="Pragma")
    void SetGameMode(const EPragma_Party_GameMode& GameModeSelection);
    
  2. Define SetGameModeSelection() in the MyPlayerController.cpp file to change a party’s game mode selection. You’ll notice we also added a check to verify the player setting the game mode is the party leader.

    void AMyPlayerController::SetGameMode(const EPragma_Party_GameMode& GameModeSelection)
    {
    	if(Player->PartyApi().GetPartyCache()->Party()->IsLeader(Player->Id())==true)
    	{
    		FPragma_Party_ExtUpdatePartyRequest Request;
    		Request.Update.SetNewGameMode(GameModeSelection);
    
    		UPragmaPartyApi::FOnCompleteDelegate UpdatePartyDelegate;
    
    		UpdatePartyDelegate.BindWeakLambda(this, [=, this](TPragmaResult<> Result)
    		{
    			if (Result.IsSuccessful())
    			{
    				UE_LOG(LogTemp, Display,
    					TEXT("Changed game mode selection to %s"), *UEnum::GetValueAsString<EPragma_Party_GameMode>(GameModeSelection));
    			}
    			else
    			{
    				UE_LOG(LogTemp, Warning,
    					TEXT("Unable to change game mode: %s"), *Result.Error().ToString());
    			}
    		});
    
    		Player->PartyApi().UpdateParty(Request, UpdatePartyDelegate);
    	}
    	else
    	{
    		UE_LOG(LogTemp, Display,TEXT("Only party leaders can select game mode"));
    	}
    }
    

Our SetGameMode() function accepts one of the GameMode values that we defined and made available on the party via the ExtParty proto (see Define party and player options). As long as the player is a party leader, the GameModeSelection parameter is set as the game mode and sent to the PartyApi’s UpdateParty() function with the new game mode as the ExtUpdatePartyRequest payload. The UpdateParty() function facilitates updating the party data on the backend.

Test #

To test this functionality using the Unreal in-game console, as the party leader issue the SetGameMode() function with either CASUAL or RANKED. If your selection is valid, you’ll see a “Changed game mode selection to [your game mode selection]” in the Unreal output log.

To apply this functionality using Unreal Blueprints:

  1. Create a “Casual” button and a “Ranked” button.

  2. Have the “Casual” button call your SetGameMode() function with a EPragma Party Game Mode variable defaulted to CASUAL.

  3. Have the “Ranked” button call your SetGameMode() function with a EPragma Party Game Mode variable defaulted to RANKED.

  4. Output the selection to the player’s screen.

Make character selections #

Goal #

Implement a SetCharacter() function that allows players to select characters.

In an actual game, there are many selections that players can make prior to getting in game. To keep our example simple, we’ll limit what the player can do to making a character selection.

Steps #

  1. Declare SetCharacter() in the public section of the MyPlayerController.h file. Have the function accept a character value based on the enum we defined in Define party and player options:

    UFUNCTION(Exec, BlueprintCallable, meta=(Category="Pragma"))
    void SetCharacter(const EPragma_Party_Character& CharacterSelection);
    
  2. Define SetCharacter() in the MyPlayerController.cpp file to change a player’s character selection:

    void AMyPlayerController::SetCharacter(const EPragma_Party_Character& CharacterSelection)
    {
    	UPragmaPartyApi::FOnCompleteDelegate UpdatePartyPlayerDelegate;
    	UpdatePartyPlayerDelegate.BindWeakLambda(this, [=, this](TPragmaResult<> Result)
    	{
    		if (Result.IsSuccessful())
    		{
    			UE_LOG(LogTemp, Display,
    				TEXT("Changed character selection to %s"), *UEnum::GetValueAsString<EPragma_Party_Character>(CharacterSelection));
    		}
    		else
    		{
    			UE_LOG(LogTemp, Warning,
    				TEXT("Unable to change character: %s"), *Result.Error().ToString());
    		}
    	});
    
    	FPragma_Party_ExtUpdatePartyPlayerRequest Request;
    	Request.Update.SetNewCharacter(CharacterSelection);
    
    	Player->PartyApi().UpdatePartyPlayer(Request, UpdatePartyPlayerDelegate);
    }
    

Our SetCharacter() function accepts one of the Character values that we defined and made available on the party via the ExtPartyPlayer proto (see Define party and player options). The CharacterSelection parameter is set as the player’s character and sent to the PartyApi’s UpdatePartyPlayer() function with the new character as the ExtUpdatePartyPlayerRequest payload. The UpdatePartyPlayer() function facilitates updating the player data on the backend. In addition, the player’s isReady value is automatically set to true, which allows the player to enter matchmaking.

Test #

To test this functionality using the Unreal in-game console, as a player in a party, issue the SetCharacter() function with either KNIGHT, MAGE, or ROGUE. If your selection is valid, you’ll see a “Changed character selection to [your character selection]” in the Unreal output log.

To apply this functionality using Unreal Blueprints:

  1. Create a button for each character option.

  2. Have each button call your SetGameMode() function with a EPragma Party Game Mode variable defaulted to the respective character choice (e.g. MAGE).

  3. Output the selection to the player’s screen.

Leave the party #

Goal #

Implement a LeaveParty() function that allows players to leave a party.

Steps #

  1. Declare the LeaveParty() function in the public section of the MyPlayerController.h file:

    UFUNCTION(Exec, BlueprintCallable, Category="Pragma")
    void LeaveParty();
    
  2. Define LeaveParty() in the MyPlayerController.cpp file:

    void AMyPlayerController::LeaveParty()
    {
    	UPragmaPartyApi::FOnCompleteDelegate LeavePartyDelegate;
    	LeavePartyDelegate.BindWeakLambda(this, [this](TPragmaResult<> Result)
    	{
    		if (Result.IsSuccessful())
    		{
    			UE_LOG(LogTemp, Display, TEXT("Successfully left party."));
    		}
    		else
    		{
    			UE_LOG(LogTemp, Warning,
    				TEXT("Unable to leave party: %s"), *Result.Error().ToString());
    		}
    	});
    	Player->PartyApi().LeaveParty(LeavePartyDelegate);
    }
    

Our LeaveParty() function calls the Pragma PartyApi’s LeaveParty() function, which facilitates removing the player from the party.

Test #

To test this functionality using the Unreal in-game console, as a player currently in a party, issue LeaveParty. If successful, you’ll see a “Successfully left party.” message your the Unreal output log.

To apply this functionality using Unreal Blueprints, create a “Leave party” button that calls your LeaveParty() function.

Sample header and source files #

The following sample files combine the code blocks from this tutorial, along with the BeginPlay(), LogIn() and LogOut() functions from the Handle Login and Logout tutorial.

Sample MyPlayerController.h header file
// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/PlayerController.h"
#include "PragmaPtr.h"
#include "PragmaGameInstanceSubsystem.h"
#include "PragmaResult.h"
#include "Dto/PragmaGameInstanceExtDto.h"
#include "Dto/PragmaPartyRpcExtDto.h"
#include "Services/Party/PragmaParty.h"
#include "PragmaLocalPlayerSubsystem.h"
#include "Services/Party/PragmaPartyInvite.h"
#include "MyPlayerController.generated.h"


// Forward declares Pragma pointer types for Pragma::FPlayer.
PRAGMA_FWD(FPlayer);

UCLASS()
class UNREALTUTORIAL_API AMyPlayerController : public APlayerController
{
	GENERATED_BODY()

public:
    
	virtual void BeginPlay() override;

	// Weak pointer to our Pragma Player owned by the PragmaLocalPlayerSubsystem.
	Pragma::FRuntimePtr Runtime;
	Pragma::FPlayerPtr Player;

	//Login and Logout functions
	
	UFUNCTION(Exec, BlueprintCallable, Category="Pragma")
	void LogIn(const FString& Username);

	UFUNCTION(Exec, BlueprintCallable, meta=(Category="Pragma"))
	void LogOut();

	//Initialization functions

	UFUNCTION(Exec, BlueprintCallable, Category="Pragma")
	void InitializeParty();
	
	//Party functions
	
	UFUNCTION(Exec, BlueprintCallable, Category="Pragma")
	void CreateParty();
	
	UFUNCTION(Exec, BlueprintCallable, Category="Pragma")
	void JoinPartyWithInviteCode(const FString& InviteCode);

	UFUNCTION(Exec, BlueprintCallable, Category="Pragma")
	void JoinPartyWithPartyId(const FString& PartyId);
	
	UFUNCTION(Exec, BlueprintCallable, Category="Pragma")
	void SendPartyInviteByPlayerId(const FString& InviteeId);

	UFUNCTION(Exec, BlueprintCallable, meta=(Category="Pragma"))
	void RespondToPartyInvite(const FString& PartyInviteId, const bool response);

	UFUNCTION(Exec, BlueprintCallable, Category="Pragma")
	void SetGameMode(const EPragma_Party_GameMode& GameModeSelection);

	UFUNCTION(Exec, BlueprintCallable, meta=(Category="Pragma"))
	void SetCharacter(const EPragma_Party_Character& CharacterSelection);

	UFUNCTION(Exec, BlueprintCallable, meta=(Category="Pragma"))
	void LeaveParty();
	
	
private:
	void HandleLoggedIn(const TPragmaResult<>& Result);
	void HandleLoggedOut();
	void HandleOnPartyInviteReceived(const FPragmaPartyInvite& MyPartyInvite);
};
Sample MyPlayerController.cpp source file
// Fill out your copyright notice in the Description page of Project Settings.

#include "MyPlayerController.h"

#include "PragmaPlayer.h"
#include "Services/Party/PragmaParty.h"
#include "Dto/PragmaAccountRpcDto.h"
#include "Dto/PragmaPartyRpcExtDto.h"
#include "Dto/PragmaPresenceExtDto.h"
#include "Services/Party/PragmaPartyInvite.h"
#include "PragmaLocalPlayerSubsystem.h"

void AMyPlayerController::BeginPlay()
{
	Super::BeginPlay();

	UE_LOG(LogTemp, Display, TEXT("Initializing") );
	
	const auto* Subsystem = GetLocalPlayer()->GetSubsystem<UPragmaLocalPlayerSubsystem>();

	// The Pragma Runtime is automatically initialized in the PragmaGameInstanceSubsystem.
	auto RuntimeA = Subsystem->Runtime();

	// Set configuration for the SDK before logging in.
	RuntimeA->Config().BackendAddress = "http://127.0.0.1:10000";
	RuntimeA->Config().ProtocolType = EPragmaProtocolType::WebSocket;
	RuntimeA->Config().GameClientVersion = "GameServerVersion1";

	// The UPragmaLocalPlayerSubsystem is automatically initialized
	// with a Pragma Player object for every LocalPlayer.
	Player = Subsystem->Player();

	Player->PartyApi().OnPartyInviteReceived.AddUObject(
		this, &AMyPlayerController::HandleOnPartyInviteReceived);
	
}

//Login and Logout functions

void AMyPlayerController::LogIn(const FString& Username)
{
    Player->LogIn(
        EPragma_Account_IdProvider::UNSAFE,
        Username,
        Pragma::FPlayer::FLoggedInDelegate::CreateUObject(
            this, &AMyPlayerController::HandleLoggedIn));
}

void AMyPlayerController::LogOut()
{
    Player->LogOut(Pragma::FPlayer::FLoggedOutDelegate::CreateUObject(
        this, &AMyPlayerController::HandleLoggedOut));
}

//Initialization functions

void AMyPlayerController::InitializeParty()
{
	UPragmaPartyApi::FOnCompleteDelegate PartyInitializeDelegate;
	PartyInitializeDelegate.BindWeakLambda(this, [this](const TPragmaResult<>& Result)
		{
			if (Result.IsSuccessful())
			{
				UE_LOG(LogTemp, Display, TEXT("Party service initialized."));
			}
			else
			{
				UE_LOG(LogTemp, Warning, TEXT("Party service failed to initialize: %s"), *Result.Error().ToString());
			}
		});
	Player->PartyApi().Initialize(PartyInitializeDelegate);
}


//Party functions

void AMyPlayerController::CreateParty()
{
	UPragmaPartyApi::FOnCompleteDelegate OnPartyCreatedDelegate;
	OnPartyCreatedDelegate.BindWeakLambda(this, [this](TPragmaResult<> Result)
	{
		if (Result.IsSuccessful())
		{
			UE_LOG(LogTemp, Display, TEXT("Pragma party created.  \n Party ID: %s \n Invite code: %s"),
				*Player->PartyApi().GetPartyCache()->Party()->GetId(),
				*Player->PartyApi().GetPartyCache()->Party()->GetInviteCode());
		}
		else
		{
			UE_LOG(LogTemp, Warning, TEXT("Pragma unable to create party: %s"), *Result.Error().ToString());
		}
	});

	Player->PartyApi().CreateParty(
		FPragma_Party_ExtCreateRequest{},
		FPragma_Party_ExtPlayerJoinRequest{},
		TArray<FString>(),
		TMap<FString, int32>(),
		OnPartyCreatedDelegate);
}

void AMyPlayerController::JoinPartyWithInviteCode(const FString& InviteCode)
{
	UPragmaPartyApi::FOnCompleteDelegate JoinWithInviteCodeDelegate;
	JoinWithInviteCodeDelegate.BindWeakLambda(this, [=, this](TPragmaResult<> Result)
	{
		if (Result.IsSuccessful())
		{
			UE_LOG(LogTemp, Display, TEXT("Joined party using invite code %s"), *InviteCode);
		}
		else
		{
			UE_LOG(LogTemp, Warning,
				TEXT("Unable to join party: %s"), *Result.Error().ToString());
		}
	});

	Player->PartyApi().JoinPartyWithInviteCode(
		FPragma_Party_ExtPlayerJoinRequest{},
		InviteCode,
		JoinWithInviteCodeDelegate
	);
}

void AMyPlayerController::JoinPartyWithPartyId(const FString& PartyId)
{
	UPragmaPartyApi::FOnCompleteDelegate JoinWithPartyIdDelegate;
	JoinWithPartyIdDelegate.BindWeakLambda(this, [=, this](TPragmaResult<> Result)
	{
		if (Result.IsSuccessful())
		{
			UE_LOG(LogTemp, Display, TEXT("Joined party using party ID %s"), *PartyId);
		}
		else
		{
			UE_LOG(LogTemp, Warning,
				TEXT("Unable to join party: %s"), *Result.Error().ToString());
		}
	});

	Player->PartyApi().JoinPartyWithId(
		FPragma_Party_ExtPlayerJoinRequest{},
		PartyId,
		JoinWithPartyIdDelegate
	);
}

void AMyPlayerController::SendPartyInviteByPlayerId(const FString& InviteeId)
{

	UPragmaPartyApi::FOnInviteSentDelegate SendPartyInviteDelegate;
	SendPartyInviteDelegate.BindWeakLambda(
		this, [this, InviteeId](const TPragmaResult<FString>& SendPartyInviteResult)
	{
		if (SendPartyInviteResult.IsSuccessful())
		{
			UE_LOG(LogTemp, Display, TEXT("Party invite sent to player ID: %s"), *InviteeId);
		}
		else
		{
			UE_LOG(LogTemp, Warning,
				TEXT("Unable to send invite to player ID: %s Reason: %s"), *InviteeId, *SendPartyInviteResult.Error().ToString());
		}
	});
	
	Player->PartyApi().SendPartyInvite(
		InviteeId,
		SendPartyInviteDelegate
	);
}

void AMyPlayerController::RespondToPartyInvite(const FString& PartyInviteId, const bool Response)
{
	UPragmaPartyApi::FOnCompleteDelegate RespondInviteDelegate;
	RespondInviteDelegate.BindWeakLambda(this, [=, this](TPragmaResult<> Result)
	{
		if (Result.IsSuccessful())
		{
			if (Response==true)
			{
				UE_LOG(LogTemp, Display, TEXT("Accepted party invite. Party successfully joined."));
			}
			else
			{
				UE_LOG(LogTemp, Display, TEXT("Declined party invite"));
			}
		}
		else
		{
			UE_LOG(LogTemp, Warning,
				TEXT("Unable to respond: %s"), *Result.Error().ToString());
		}
	});

	Player->PartyApi().RespondToPartyInvite(
		FPragma_Party_ExtPlayerJoinRequest{},
		PartyInviteId,
		Response,
		RespondInviteDelegate
	);
}

void AMyPlayerController::SetGameMode(const EPragma_Party_GameMode& GameModeSelection)
{
	if(Player->PartyApi().GetPartyCache()->Party()->IsLeader(Player->Id())==true)
	{
		FPragma_Party_ExtUpdatePartyRequest Request;
		Request.Update.SetNewGameMode(GameModeSelection);

		UPragmaPartyApi::FOnCompleteDelegate UpdatePartyDelegate;
	
		UpdatePartyDelegate.BindWeakLambda(this, [=, this](TPragmaResult<> Result)
		{
			if (Result.IsSuccessful())
			{
				UE_LOG(LogTemp, Display,
					TEXT("Changed game mode selection to %s"), *UEnum::GetValueAsString<EPragma_Party_GameMode>(GameModeSelection));
			}
			else
			{
				UE_LOG(LogTemp, Warning,
					TEXT("Unable to change game mode: %s"), *Result.Error().ToString());
			}
		});

		Player->PartyApi().UpdateParty(Request, UpdatePartyDelegate);
	}
	else
	{
		UE_LOG(LogTemp, Display,TEXT("Only party leaders can select game mode"));
	}
}

void AMyPlayerController::SetCharacter(const EPragma_Party_Character& CharacterSelection)
{
	UPragmaPartyApi::FOnCompleteDelegate UpdatePartyPlayerDelegate;
	UpdatePartyPlayerDelegate.BindWeakLambda(this, [=, this](TPragmaResult<> Result)
	{
		if (Result.IsSuccessful())
		{
			UE_LOG(LogTemp, Display,
				TEXT("Changed character selection to %s"), *UEnum::GetValueAsString<EPragma_Party_Character>(CharacterSelection));
		}
		else
		{
			UE_LOG(LogTemp, Warning,
				TEXT("Unable to change character: %s"), *Result.Error().ToString());
		}
	});

	FPragma_Party_ExtUpdatePartyPlayerRequest Request;
	Request.Update.SetNewCharacter(CharacterSelection);

	Player->PartyApi().UpdatePartyPlayer(Request, UpdatePartyPlayerDelegate);
}

void AMyPlayerController::LeaveParty()
{
	UPragmaGameLoopApi::FOnCompleteDelegate LeavePartyDelegate;
	LeavePartyDelegate.BindWeakLambda(this, [this](TPragmaResult<> Result)
	{
		if (Result.IsSuccessful())
		{
			UE_LOG(LogTemp, Display, TEXT("Successfully left party."));
		}
		else
		{
			UE_LOG(LogTemp, Warning,
				TEXT("Unable to leave party: %s"), *Result.Error().ToString());
		}
	});
	Player->PartyApi().LeaveParty(LeavePartyDelegate);
}



//Handler functions

void AMyPlayerController::HandleLoggedIn(const TPragmaResult<>& Result)
{
    if (Result.IsFailure())
    {
        UE_LOG(LogTemp, Display, TEXT("Pragma -- Login failed: %s"), *Result.ErrorCode());
    }
    UE_LOG(LogTemp, Display, TEXT("Pragma -- Logged in."));
}

void AMyPlayerController::HandleLoggedOut()
{
    UE_LOG(LogTemp, Display, TEXT("Pragma -- Logged out."));
}

void AMyPlayerController::HandleOnPartyInviteReceived(const FPragmaPartyInvite& MyPartyInvite)
{
	UE_LOG(LogTemp, Display, TEXT("Party invite received. Respond using invite ID: %s"), *MyPartyInvite.GetInviteId());
}

Next steps #

At this point, players can create and join parties, make party and character selections, and leave parties using the Unreal in-game console or Blueprint graph. Continue to the Matchmaking tutorial to learn how to implement sample matchmaking functionality.