Unreal: Parties #

This tutorial uses Unreal Engine 5.3 with Pragma Engine 0.0.99 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 SDK. 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.

Prerequisites:

Get started #

To get started, re-run the update script command:

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.

Create the party #

Goal:

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

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()
    {
    	UPragmaGameLoopApi::FOnCompleteDelegate OnPartyCreatedDelegate;
    	OnPartyCreatedDelegate.BindWeakLambda(this, [this](TPragmaResult<> Result)
    	{
    		if (Result.IsSuccessful())
    		{
    			UE_LOG(LogTemp, Display, TEXT("Pragma party created."));
    		}
    		else
    		{
    			UE_LOG(LogTemp, Warning, TEXT("Pragma unable to create party: %s"), *Result.Error().ToString());
    		}
    	});
    
    	Player->GameLoopApi().CreateParty(
    		FPragma_Party_ExtCreateRequest{},
    		FPragma_Party_ExtPlayerJoinRequest{},
    		TArray<FString>(),
    		TMap<FString, int32>(),
    		OnPartyCreatedDelegate);
    }
    

Our CreateParty() function simply calls the Pragma GameLoopApi’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.

Test:

To test this functionality using the Unreal in-game console, call CreateParty() as a logged-in user. You should see “Pragma party created.” in your Unreal output log.

To apply this functionality using Unreal Blueprints, create a button that calls your CreateParty() function when clicked.

Join the party with invite code #

Goal:

Define the functions that will allow a player to join an existing party using the party code.

Upon party creation, parties are automatically assigned a unique invite code. Now, we’ll make it possible for other players to join our party using that invite code.

Steps:

  1. To get the party’s invite code, declare a GetInviteCode() function in the public section of the MyPlayerController.h file:

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

    FString AMyPlayerController::GetInviteCode()
    {
    	if (!Player->GameLoopApi().GetParty())
    	{
    		return FString{};
    	}
    	UE_LOG(LogTemp, Display, TEXT("Pragma invite created %s"), *Player->GameLoopApi().GetParty()->GetInviteCode());
    	return Player->GameLoopApi().GetParty()->GetInviteCode();
    }
    
  3. Next, declare the JoinByInviteCode() function in the public section of the MyPlayerController.h file:

    UFUNCTION(Exec, BlueprintCallable, Category="Pragma")
    void JoinByInviteCode(const FString& InviteCode);
    
  4. Define JoinByInviteCode in the MyPlayerController.cpp file to allow players to join a party:

    void AMyPlayerController::JoinByInviteCode(const FString& InviteCode)
    {
    	UPragmaGameLoopApi::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->GameLoopApi().JoinPartyWithInviteCode(
    		FPragma_Party_ExtPlayerJoinRequest{},
    		InviteCode,
    		JoinWithInviteCodeDelegate
    	);
    }
    

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 then fetch the invite code using GetInviteCode.

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

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

To apply this functionality using Unreal Blueprints:

  1. Create a button that calls your GetInviteCode() function when clicked. Pipe the return value to a text box so players can view the code.

  2. Create an editable text box where a user can input a party invite code.

  3. Create a button that calls your JoinByInviteCode() function when clicked.

  4. Grab the text from the input box and plug it into the JoinByInviteCode node.

Send party invite #

Goal

Implement a SendPartyInviteByUserId() function that allows a player to send a party invite to another player using the player’s ID.

Steps

  1. Declare a SendPartyInviteByUserId() function in the public section of the MyPlayerController.h file. Have the function accept a string.

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

    void AMyPlayerController::SendPartyInviteByUserId(const FString& InviteeId)
    {
    	Player->GameLoopApi().SendPartyInvite(
    		InviteeId,
    		UPragmaGameLoopApi::FOnInviteSentDelegate::CreateWeakLambda(
    	this, [this, InviteeId](const TPragmaResult<FString>& SendPartyInviteResult)
    	{
    		if (SendPartyInviteResult.IsSuccessful())
    		{
    			const FString InviteId = SendPartyInviteResult.Payload();
    			UE_LOG(LogTemp, Display, TEXT("Send party invite by player id succeeded. Party invite ID: %s"), *InviteId);
    		}
    		else
    		{
    			UE_LOG(LogTemp, Warning, TEXT("Pragma unable to accept invite: %s"), *SendPartyInviteResult.Error().ToString());
    		}
    	})
    	);
    }
    

Our SendPartyInviteByUserId() function accepts a string value representing the player ID of the invitee. Using this ID, the function calls the GameLoopApi’s SendPartyInvite() function which facilitates sending an invite to the identified player. On success, our function logs a success message to the Unreal output log.

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.

  3. As test02, use GetPlayerId and record the value.

  4. As test01, use SendPartyInviteByUserId with the ID from step 3.

You should see a success message in your output log.

To apply this functionality using Unreal Blueprints:

  1. Create a button that calls your GetPlayerId() function when clicked. Pipe the return value to a text box so players can view their ID number.

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

  3. Create a button that calls your SendPartyInviteByUserId() function when clicked.

  4. Grab the text from the input box and plug it into the SendPartyInviteByUserId node.

Accept party invite #

Goal

Implement a GetPartyInvites() and RespondToPartyInvite() function that allows a player to fetch pending party invites and accept or reject an invite.

Steps

  1. To get a list of pending party invites for the active player, declare a GetPartyInvites() function in the public section of the MyPlayerController.h file:

    UFUNCTION(Exec, BlueprintCallable, Category="Pragma")
    TArray<FString> GetPartyInvites();
    
  2. Define GetPartyInvites() in the MyPlayerController.cpp file:

    TArray<FString> AMyPlayerController::GetPartyInvites()
    {
    	const TArray<FPragmaPartyInvite> PartyInvites = Player->GameLoopApi().GetPendingPartyInvites();
    
    	TArray<FString> PartyInviteCodes;
    
    	for(const FPragmaPartyInvite PartyInvite : PartyInvites)
    	{
    		FString PartyInviteCode = PartyInvite.GetInviteId();
    		UE_LOG(LogTemp, Display, TEXT("Party invite id: %s"), *PartyInviteCode);
    		PartyInviteCodes.Add(PartyInviteCode);
    	}
    
    	return PartyInviteCodes;
    }
    
  3. Allow a player to accept or reject 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, Category="Pragma")
    void RespondToPartyInvite(const FString& PartyInviteId, const bool Response);
    
  4. Define RespondToPartyInvite() in the MyPlayerController.cpp file:

    void AMyPlayerController::RespondToPartyInvite(const FString& PartyInviteId, const bool Response)
    {
    	UPragmaGameLoopApi::FOnCompleteDelegate RespondInviteDelegate;
    	RespondInviteDelegate.BindWeakLambda(this, [=, this](TPragmaResult<> Result)
    	{
    		if (Result.IsSuccessful())
    		{
    			if (Response==true)
    			{
    				UE_LOG(LogTemp, Display, TEXT("Accepted party invite id %s. Party successfully joined."), *PartyInviteId);
    			}
    			else
    			{
    				UE_LOG(LogTemp, Display, TEXT("Declined party invite id %s"), *PartyInviteId);
    			}
    		}
    		else
    		{
    			UE_LOG(LogTemp, Warning,
    				TEXT("Unable to respond: %s"), *Result.Error().ToString());
    		}
    	});
    
    	Player->GameLoopApi().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 GameLoopApi’s RespondToPartyInvite() function, which facilitates adding the invitee to the party if the invite is accepted.

Test:

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

  1. As test02, call GetPartyInvites(). The output log should show a party invite ID for the party invite test01 sent in the previous section. Record this value.

  2. As test02, call RespondToPartyInvite() with the party invite ID you fetched in step 1, along with the value of true (accept) or false (reject).

You should see the appropriate message in your output log. If you accepted the party invite, you can verify you joined the party by selecting a character.

To apply this functionality using Unreal Blueprints:

  1. Create a “Get party invites” button that calls your GetPartyInvites() function.

  2. Grab the results from the GetPartyInvites() function and display the strings on the player’s screen.

  3. Create an “Accept party invite” button and a “Reject party invite” button, along with an editable text box for the player to enter the invite code.

  4. Have the accept button call your RespondToPartyInvite() function with the entered code and a true boolean value.

  5. Have the reject button call your RespondToPartyInvite() function with the entered code and a false boolean value.

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->GameLoopApi().IsLeaderOfParty(Player->Id())==true)
    	{
    		FPragma_Party_ExtUpdatePartyRequest Request;
    		Request.Update.SetNewGameMode(GameModeSelection);
    
    		UPragmaGameLoopApi::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->GameLoopApi().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 GameLoopApi’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, 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)
    {
    	UPragmaGameLoopApi::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->GameLoopApi().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 GameLoopApi’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 PC_TutorialPlayerController.h file:

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

    void APC_TutorialPlayerController::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->GameLoopApi().LeaveParty(LeavePartyDelegate);
    }
    

Our LeaveParty() function calls the Pragma GameLoopApi’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.

Get party and player information #

Goal:

At this point, we’ve built out all the necessary party functionality, but players can’t view the state of the party. Let’s build the UI so that the state of the party is visible.

Steps:

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

    UFUNCTION(Exec, BlueprintCallable, Category="Pragma")
    FString GetDisplayName();
    
    UFUNCTION(Exec, BlueprintCallable, Category="Pragma")
    TArray<FString> GetPartyPlayerDisplayNames();
    
  2. Define GetDisplayName() and GetPartyPlayerDisplayNames() in the MyPlayerController.cpp file to retrieve the one or more players’ display name:

    FString APC_TutorialPlayerController::GetDisplayName()
    {
    	return Player->DisplayName();
    }
    
    TArray<FString> APC_TutorialPlayerController::GetPartyPlayerDisplayNames()
    {
    	if (!Player->GameLoopApi().GetParty())
    	{
    		return TArray<FString>{};
    	}
    
    	const TArray<UPragmaPartyPlayer*> PartyPlayers =
    		Player->GameLoopApi().GetParty()->GetPlayers();
    
    	TArray<FString> DisplayNames;
    
    	const FString& YourPlayerId = Player->Id();
    
    	for (const UPragmaPartyPlayer* PartyPlayer : PartyPlayers)
    	{
    		FString DisplayName = PartyPlayer->GetDisplayName().DisplayName;
    		FString PlayerId = PartyPlayer->GetPlayerId();
    
    		if (PlayerId == YourPlayerId)
    		{
    			DisplayName += " (You)";
    		}
    
    		if (PartyPlayer->IsLeader())
    		{
    			DisplayName += " (Leader)";
    		}
    
    		if (PartyPlayer->IsReady())
    		{
    			DisplayName += " (Ready)";
    		}
    
    		DisplayNames.Add(DisplayName);
    	}
    
    	return DisplayNames;
    }
    

Our GetDisplayName() function returns the Player’s DisplayName value. GetPartyPlayerDisplayNames() iterates through the players in the party and adds each player’s DisplayName to a DisplayNames array, which is then returned.

Test

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

  1. As a logged-in player, issue GetDisplayName to output your display name to the Unreal log.

  2. While in a party, issue GetDisplayNames to list the display names of all players in the party.

To apply this functionality using Unreal Blueprints:

  1. Create a “Get my display name” button that calls your GetDisplayName() function.

  2. Grab the result from the GetDisplayName() function and display the string on the player’s screen.

  3. Create a “Get player display names” button that calls your GetDisplayNames() function.

  4. Grab the results from the GetDisplayNames() function and display the strings on the player’s screen.

Handle party changes #

Goal:

Create an Unreal function that logs to the console whenever the party or party player state changes.

The Pragma SDK provides an OnPartyChanged event that fires every time the party’s state changes. We can use this event to trigger a handler function for party updates in Unreal. For now, our HandlePartyUpdate() function simply prints an Unreal log entry.

Steps:

  1. Declare HandlePartyUpdate() in the private section of your MyPlayerController.h file:

    void HandlePartyUpdate(const UPragmaParty* Party);
    
  2. Define HandlePartyUpdate() in MyPlayerController.cpp:

    void AMyPlayerController::HandlePartyUpdate(const UPragmaParty* Party)
    {
    	UE_LOG(LogTemp, Display, TEXT("Party state updated."));
    }
    
  3. Register an event handler for OnPartyChanged that calls HandlePartyUpdate(). Add the following to the end of your BeginPlay() function in your MyPlayerController.cpp:

    Player->GameLoopApi().OnPartyChanged.AddUObject(
    	this, &APC_TutorialPlayerController::HandlePartyUpdate);
    

Whenever the party’s state changes, OnPartyChanged is triggered and calls HandlePartyUpdate(), which prints “Party state updated.” to your output log.

Test

To test this functionality using the Unreal in-game console, issue any of the Unreal functions that changes a party’s state, such as CreateParty() or SetCharacter(). When successfully executed, you’ll see “Party state updated.” in your Unreal output log.

To apply this functionality using Unreal Blueprints, click any of the buttons that invoke change a party’s state, such as “Set game mode” or “Set character”. When successfully executed, you’ll see “Party state updated.” in your Unreal output log along with any on-screen functionality you defined in the Blueprints.

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
#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 "Pragma/Api/Player/PragmaFriendOverview.h"
#include "Services/Party/PragmaParty.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;
    
	UFUNCTION(Exec, BlueprintCallable, Category="Pragma")
	void LogIn(const FString& Username);

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

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

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

	UFUNCTION(Exec, BlueprintCallable, meta=(Category="Pragma"))
	TArray<FString> GetPartyInvites();
	
	UFUNCTION(Exec, BlueprintCallable, meta=(Category="Pragma"))
	FString GetInviteCode();
	
	UFUNCTION(Exec, BlueprintCallable, meta=(Category="Pragma"))
	void JoinByInviteCode(const FString& InviteCode);
	
	UFUNCTION(Exec, BlueprintCallable, meta=(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"))
	FString GetDisplayName();

	UFUNCTION(Exec, BlueprintCallable, meta=(Category="Pragma"))
	FString GetPlayerId();

	UFUNCTION(Exec, BlueprintCallable, meta=(Category="Pragma"))
	TArray<FString> GetPartyPlayerDisplayNames();

	UFUNCTION(Exec, BlueprintCallable, meta=(Category="Pragma"))
	void LeaveParty();
	
	
private:
	void HandleLoggedIn(const TPragmaResult<>& Result);
	void HandleLoggedOut();
	void HandlePartyUpdate(const UPragmaParty* Party);

	// Weak pointer to our Pragma Player owned by the PragmaLocalPlayerSubsystem.
	Pragma::FRuntimePtr Runtime;
	Pragma::FPlayerPtr Player;
	
};
Sample MyPlayerController.cpp source file

#include "MyPlayerController.h"
#include "PragmaPlayer.h"
#include "Services/Party/PragmaParty.h"
#include "Dto/PragmaAccountRpcDto.h"
#include "Dto/PragmaAccountServiceRaw.h"
#include "Dto/PragmaPartyRpcExtDto.h"
#include "Pragma/Api/Player/PragmaFriendOverview.h"
#include "PragmaLocalPlayerSubsystem.h"

//using FSentInvites = TMap<FString, FPragmaFriendOverview>;

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->GameLoopApi().OnPartyChanged.AddUObject(this, &AMyPlayerController::HandlePartyUpdate);

}

//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));
}

//Party functions

void AMyPlayerController::CreateParty()
{
	UPragmaGameLoopApi::FOnCompleteDelegate OnPartyCreatedDelegate;
	OnPartyCreatedDelegate.BindWeakLambda(this, [this](TPragmaResult<> Result)
	{
		if (Result.IsSuccessful())
		{
			UE_LOG(LogTemp, Display, TEXT("Pragma party created."));
		}
		else
		{
			UE_LOG(LogTemp, Warning, TEXT("Pragma unable to create party: %s"), *Result.Error().ToString());
		}
	});

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

FString AMyPlayerController::GetInviteCode()
{
	if (!Player->GameLoopApi().GetParty())
	{
		return FString{};
	}
	UE_LOG(LogTemp, Display, TEXT("Invite code: %s"), *Player->GameLoopApi().GetParty()->GetInviteCode());
	return Player->GameLoopApi().GetParty()->GetInviteCode();
}

void AMyPlayerController::JoinByInviteCode(const FString& InviteCode)
{
	UPragmaGameLoopApi::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->GameLoopApi().JoinPartyWithInviteCode(
		FPragma_Party_ExtPlayerJoinRequest{},
		InviteCode,
		JoinWithInviteCodeDelegate
	);
}

void AMyPlayerController::SendPartyInviteByUserId(const FString& InviteeId)
{
	Player->GameLoopApi().SendPartyInvite(
		InviteeId,
		UPragmaGameLoopApi::FOnInviteSentDelegate::CreateWeakLambda(
	this, [this, InviteeId](const TPragmaResult<FString>& SendPartyInviteResult)
	{
		if (SendPartyInviteResult.IsSuccessful())
		{
			const FString InviteId = SendPartyInviteResult.Payload();
			UE_LOG(LogTemp, Display, TEXT("Send party invite by player id succeeded. Party invite ID: %s"), *InviteId);
		}
		else
		{
			UE_LOG(LogTemp, Warning, TEXT("Pragma unable to accept invite: %s"), *SendPartyInviteResult.Error().ToString());
		}
	})
	);
}

TArray<FString> AMyPlayerController::GetPartyInvites()
{
	const TArray<FPragmaPartyInvite> PartyInvites = Player->GameLoopApi().GetPendingPartyInvites();

	TArray<FString> PartyInviteIds;

	for(const FPragmaPartyInvite PartyInvite : PartyInvites)
	{
		FString PartyInviteId = PartyInvite.GetInviteId();
		UE_LOG(LogTemp, Display, TEXT("Invitation id: %s"), *PartyInviteId);
		PartyInviteIds.Add(PartyInviteId);
	}
	
	return PartyInviteIds;
}

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

	Player->GameLoopApi().RespondToPartyInvite(
		FPragma_Party_ExtPlayerJoinRequest{},
		PartyInviteId,
		response,
		RespondInviteDelegate
	);
}

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

		UPragmaGameLoopApi::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->GameLoopApi().UpdateParty(Request, UpdatePartyDelegate);
	}
	else
	{
		UE_LOG(LogTemp, Display,TEXT("Only party leaders can select game mode"));
	}
}

void AMyPlayerController::SetCharacter(const EPragma_Party_Character& CharacterSelection)
{
	UPragmaGameLoopApi::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->GameLoopApi().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->GameLoopApi().LeaveParty(LeavePartyDelegate);
}


// Player data functions

FString AMyPlayerController::GetDisplayName()
{
	UE_LOG(LogTemp, Display, TEXT("Your username: %s"), *Player->DisplayName());
	return Player->DisplayName();
}

TArray<FString> AMyPlayerController::GetPartyPlayerDisplayNames()
{
	const TArray<UPragmaPartyPlayer*> PartyPlayers =
		Player->GameLoopApi().GetParty()->GetPlayers();

	TArray<FString> DisplayNames;

	const FString& YourPlayerId = Player->Id();

	for (const UPragmaPartyPlayer* PartyPlayer : PartyPlayers)
	{
		FString DisplayName = PartyPlayer->GetDisplayName().DisplayName;
		FString PlayerId = PartyPlayer->GetPlayerId();

		if (PlayerId == YourPlayerId)
		{
			DisplayName += " (You)";
		}

		if (PartyPlayer->IsLeader())
		{
			DisplayName += " (Leader)";
		}

		if (PartyPlayer->IsReady())
		{
			DisplayName += " (Ready)";
		}

		UE_LOG(LogTemp, Display, TEXT("Party player: %s"), *DisplayName);
		
		DisplayNames.Add(DisplayName);
	}
		
	return DisplayNames;
}

FString AMyPlayerController::GetPlayerId()
{
	UE_LOG(LogTemp, Display, TEXT("Your id: %s"), *Player->Id());
	return Player->Id();
}

//Handler functions

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

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

void AMyPlayerController::HandlePartyUpdate(const UPragmaParty* Party)
{
	UE_LOG(LogTemp, Display, TEXT("Party state updated."));
}

Continue on #

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.