Unreal: Social #

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

In this tutorial, we’ll expand the MyPlayerController.h header file and MyPlayerController.cpp source file to implement functionality related to the Pragma Engine Presence service. Specifically we will:

  • Allow a player to appear “online” or “offline” (basic presence)
  • Allow a player to appear “in shop” (rich presence)
  • Display a player’s current presence values

Note: We do not need a specific Unreal function for the “in party” presence, as that is automatically set by our Party Plugin (see Set Up Presence Features with Pragma Engine).

By the end of the tutorial, our player client and backend will be able to make and update basic and rich presence selections.

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 social 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, party functionality, and matchmaking functionality.

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

Implement the Initialize function #

Goal #

Implement a InitializeSocial() function that initializes the Friends and Presence services and sets the player’s basic presence to Online.

Steps #

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

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

    void AMyPlayerController::InitializeSocial()
    {
        UPragmaGameLoopApi::FOnCompleteDelegate FriendInitializeDelegate;
        FriendInitializeDelegate.BindWeakLambda(this, [this](const TPragmaResult<>& Result)
            {
                if (Result.IsSuccessful())
                {
                    UE_LOG(LogTemp, Display, TEXT("Friends and Presence services initialized."));
                }
                else
                {
                    UE_LOG(LogTemp, Warning, TEXT("Friends and Presence services failed to initialize: %s"), *Result.Error().ToString());
                }
            });
        Player->FriendApi().Initialize(FriendInitializeDelegate);
    }
    

Test #

To test this functionality using the Unreal in-game console, issue InitializeSocial as each player client (e.g. test01 and test02).

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

When the service is successfully initialized, you should see “Friends and Presence services initialized.” in the Unreal output log, along with any on-screen functionality you defined in the Blueprints (such as populating a “Basic presence” text box).

Set player presence to online or offline #

Goal #

Implement a SetPresenceToOnline() function that sets the player’s basic presence to “Online”, and and SetPresenceToAway() function that sets the player’s basic presence to “Away”.

Steps #

  1. Declare the SetPresenceToOnline() and SetPresenceToAway() functions in the public section of your MyPlayerController.h file:

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

    void AMyPlayerController::SetPresenceToOnline()
    {
        Player->PresenceApi().SetAsOnline(
            UPragmaPresenceApi::FOnCompleteDelegate::CreateWeakLambda(this, [](const TPragmaResult<>& Result)
            {
                if (Result.IsSuccessful())
                {
                    UE_LOG(LogTemp, Display, TEXT("Set presence to online succeeded."));
                }
                else
                {
                    UE_LOG(LogTemp, Display, TEXT("Set presence to online failed."));
                }
            }));
    }
    
    void AMyPlayerController::SetPresenceToAway()
    {
        Player->PresenceApi().SetAsAway(
            UPragmaPresenceApi::FOnCompleteDelegate::CreateWeakLambda(this, [](const TPragmaResult<>& Result)
            {
                if (Result.IsSuccessful())
                {
                    UE_LOG(LogTemp, Display, TEXT("Set presence to away succeeded."));
                }
                else
                {
                    UE_LOG(LogTemp, Display, TEXT("Set presence to away failed."));
                }
            }));
    }
    

Our SetPresenceToOnline() calls the PresenceApi’s SetAsOnline() function, and our SetPresenceToAway() function calls the PresenceApi’s SetAsAway() function. The PresenceApi function facilitates setting the player’s basic presence to “Online” or “Away”.

Test #

To test this functionality using the Unreal in-game console, as a logged in player with social features initialized, issue SetAsOnline or SetAsAway.

If successful, you should see “Set presence to online succeeded.” or “Set presence to away succeeded.” in the Unreal output log.

To test this functionality using Unreal Blueprints, create a “Online” button that calls SetPresenceToOnline, and a “Away” button that calls SetPresenceToAway. As a logged in player with social features initialized, click each button.

If successful, you should see “Set presence to online succeeded.” or “Set presence to away succeeded.” in the Unreal output log along with any on-screen functionality you defined in the Blueprints (such as populating a “Basic presence” text box).

Set player presence to “in shop” #

Goal #

Implement a SetInShop() function that allows a player client to set or update a player’s rich presence in_shop value.

Steps #

  1. Declare the SetInShop() function in the public section of your MyPlayerController.h file. Have it accept a boolean value that represents whether the player is currently in the shop:

    UFUNCTION(Exec, BlueprintCallable, Category="Pragma")
    void SetInShop(const bool IsInShop);
    
  2. Define SetInShop() in your MyPlayerController.cpp file:

    void AMyPlayerController::SetInShop(const bool IsInShop)
    {
    
    	FPragma_Presence_ExtRichPresencePlayerRequest Request;
    	Request.Update.SetInShop(IsInShop);
    
    	UPragmaPresenceApi::FOnCompleteDelegate SetRichPresenceDelegate;
    
    	SetRichPresenceDelegate.BindWeakLambda(this, [IsInShop](TPragmaResult<> Result)
    	{
    		if (Result.IsSuccessful())
    		{
    			UE_LOG(LogTemp, Display,
    				TEXT("Set in shop to %s"), IsInShop ? TEXT("true") : TEXT("false"));
    		}
    		else
    		{
    			UE_LOG(LogTemp, Warning,
    				TEXT("Unable to set rich presence: %s"), *Result.Error().ToString());
    		}
    	});
    
    	Player->PresenceApi().SetRichPresence(Request, SetRichPresenceDelegate);
    }
    

Our SetInShop() calls the PresenceApi’s SetRichPresence() function, providing the ExtRichPresencePlayerRequest value.

Test #

To test this functionality using the Unreal in-game console, as a logged in player with social features initialized, issue SetInShop true.

If successful, you should see “Set in shop to true.” in the Unreal output log.

To test this functionality using Unreal Blueprints, create a “Enter shop” button that calls SetInShop with the true value, and a “Leave shop” button that calls SetInShop with the false value. As a logged in player with social features initialized, click each button.

If successful, you should see “Set in shop to true.” in the Unreal output log along with any on-screen functionality you defined in the Blueprints (such as populating a “Rich presence” text box).

Display presence changes #

Goal #

Implement a HandleOnPresenceChanged() function that listens to the OnPresenceChanged event.

The Pragma SDK provides an OnPresenceChanged event that fires every time a player’s basic or rich presence is changed. We can use this event to trigger a handler function for updates in Unreal. For now, our HandleOnPresenceChanged() function simply print an Unreal log entry that displays the player’s presence values.

Steps #

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

    void HandleOnPresenceChanged(const FPragmaPresence& MyPresence);
    
  2. Define HandleOnPresenceChanged() in MyPlayerController.cpp:

    void AMyPlayerController::HandleOnPresenceChanged(const FPragmaPresence& MyPresence)
    {
        UE_LOG(LogTemp, Display, TEXT("Presence status changed: "
                "\n Basic: %s, \n Rich: %s, %s"), *UEnum::GetValueAsString<EBasicPresence>(MyPresence.BasicPresence),
            MyPresence.Ext.InShop ? TEXT("In shop") : TEXT("Not in shop"),
            MyPresence.Ext.InParty ? TEXT("In party") : TEXT("Not in party"));
    }
    
  3. Register the event handler. Add the following to the end of your BeginPlay() function in your MyPlayerController.cpp file:

    Player->PresenceApi().OnPresenceChanged.AddUObject(
    	this, &AMyPlayerController::HandleOnPresenceChanged);
    

Test #

To test this functionality using the Unreal in-game console, as a logged in player with social features initialized, change your presence.

If successful, you should see the “Presence status changed …” message in the Unreal output log, along with any on-screen functionality you defined in the Blueprints (such as populating a “Rich presence” text box).

Test backend presence changes #

Because we modified the OnAddPlayer and OnRemovePlayer methods in Automatically set player to “in party” to update a player’s rich presence based on party state, we don’t need to do any additional work in the Unreal files.

To test the functionality defined in the plugin using Unreal, as a logged in player with social features initialized, create, join, or leave a party. You should see the message defined in your HandleOnPresenceChanged function in the Unreal output log.

Sample header and source files #

The following sample files combine the code blocks from this tutorial, along with the functions from the Handle Login and Logout tutorial, the Unreal: Parties tutorial, and the Unreal: Matchmaking 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 "Pragma/Api/Player/PragmaFriendOverview.h"
#include "Pragma/Api/Player/PragmaFriendApi.h"
#include "Services/Party/PragmaParty.h"
#include "PragmaLocalPlayerSubsystem.h"
#include "Pragma/Api/Player/PragmaFriend.h"
#include "MyPlayerController.generated.h"

using FFriendList = TMap<FString, FPragmaFriend>;

// 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 InitializeGameLoop();
		
	UFUNCTION(Exec, BlueprintCallable, Category="Pragma")
	void InitializeSocial();
	
	//Party functions
	
	UFUNCTION(Exec, BlueprintCallable, Category="Pragma")
	void CreateParty();

	UFUNCTION(Exec, BlueprintCallable, Category="Pragma")
	void JoinPartyWithCode(const FString& InviteCode);
	
	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();
	
	//Matchmaking functions

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

	//Presence functions

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

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

	UFUNCTION(Exec, BlueprintCallable, Category="Pragma")
	void SetInShop(const bool IsInShop);


	//Get functions

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

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

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

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

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

	
private:
	void HandleLoggedIn(const TPragmaResult<>& Result);
	void HandleLoggedOut();
	void HandleOnAddedToGame(const FString GameInstanceId, const FPragma_GameInstance_ExtAddedToGame Ext);
	void HandleOnPresenceChanged(const FPragmaPresence& MyPresence);

};
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 "Pragma/Api/Player/PragmaFriendOverview.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->GameLoopApi().OnAddedToGame.AddUObject(
	this, &AMyPlayerController::HandleOnAddedToGame);

	Player->PresenceApi().OnPresenceChanged.AddUObject(
	this, &AMyPlayerController::HandleOnPresenceChanged);
}

//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::InitializeGameLoop()
{
	UPragmaGameLoopApi::FOnCompleteDelegate GameLoopInitializeDelegate;
	GameLoopInitializeDelegate.BindWeakLambda(this, [this](const TPragmaResult<>& Result)
		{
			if (Result.IsSuccessful())
			{
				UE_LOG(LogTemp, Display, TEXT("Game loop services initialized."));
			}
			else
			{
				UE_LOG(LogTemp, Warning, TEXT("Game loop services failed to initialize: %s"), *Result.Error().ToString());
			}
		});
	Player->GameLoopApi().Initialize(GameLoopInitializeDelegate);
}

void AMyPlayerController::InitializeSocial()
{
	UPragmaGameLoopApi::FOnCompleteDelegate FriendInitializeDelegate;
	FriendInitializeDelegate.BindWeakLambda(this, [this](const TPragmaResult<>& Result)
		{
			if (Result.IsSuccessful())
			{
				UE_LOG(LogTemp, Display, TEXT("Friends and Presence services initialized."));
			}
			else
			{
				UE_LOG(LogTemp, Warning, TEXT("Friends and Presence services failed to initialize: %s"), *Result.Error().ToString());
			}
		});
	Player->FriendApi().Initialize(FriendInitializeDelegate);
}


//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 with code: %s"), *Player->GameLoopApi().GetPartyState()->Party()->GetInviteCode());
		}
		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);
}


void AMyPlayerController::JoinPartyWithCode(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::SendPartyInviteByPlayerId(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 send invite: %s"), *SendPartyInviteResult.Error().ToString());
		}
	})
	);
}

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

void AMyPlayerController::RespondToPartyInvite(const FString& PartyInviteCode, 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."), *PartyInviteCode);
			}
			else
			{
				UE_LOG(LogTemp, Display, TEXT("Declined party invite id %s"), *PartyInviteCode);
			}
		}
		else
		{
			UE_LOG(LogTemp, Warning,
				TEXT("Unable to respond: %s"), *Result.Error().ToString());
		}
	});

	Player->GameLoopApi().RespondToPartyInvite(
		FPragma_Party_ExtPlayerJoinRequest{},
		PartyInviteCode,
		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);
}

//Matchmaking functions

void AMyPlayerController::EnterMatchmaking()
{
	UPragmaGameLoopApi::FOnCompleteDelegate OnEnterMatchmakingDelegate;
	OnEnterMatchmakingDelegate.BindWeakLambda(this, [this](TPragmaResult<> Result)
	{
		if (Result.IsSuccessful())
		{
			UE_LOG(LogTemp, Display, TEXT("Enter matchmaking success."));
		}
		else
		{
			UE_LOG(LogTemp, Warning, TEXT("Pragma unable to enter matchmaking: %s"), *Result.Error().ToString());
		}
	});

	Player->GameLoopApi().EnterMatchmaking(OnEnterMatchmakingDelegate);
}

//Presence functions

void AMyPlayerController::SetPresenceToOnline()
{
	Player->PresenceApi().SetAsOnline(
		UPragmaPresenceApi::FOnCompleteDelegate::CreateWeakLambda(this, [](const TPragmaResult<>& Result)
		{
			if (Result.IsSuccessful())
			{
				UE_LOG(LogTemp, Display, TEXT("Set presence to online succeeded."));
			}
			else
			{
				UE_LOG(LogTemp, Display, TEXT("Set presence to online failed."));
			}
		}));
}

void AMyPlayerController::SetPresenceToAway()
{
	Player->PresenceApi().SetAsAway(
		UPragmaPresenceApi::FOnCompleteDelegate::CreateWeakLambda(this, [](const TPragmaResult<>& Result)
		{
			if (Result.IsSuccessful())
			{
				UE_LOG(LogTemp, Display, TEXT("Set presence to away succeeded."));
			}
			else
			{
				UE_LOG(LogTemp, Display, TEXT("Set presence to away failed."));
			}
		}));
}


void AMyPlayerController::SetInShop(const bool IsInShop)
{

	FPragma_Presence_ExtRichPresencePlayerRequest Request;
	Request.Update.SetInShop(IsInShop);

	UPragmaPresenceApi::FOnCompleteDelegate SetRichPresenceDelegate;

	SetRichPresenceDelegate.BindWeakLambda(this, [IsInShop](TPragmaResult<> Result)
	{
		if (Result.IsSuccessful())
		{
			UE_LOG(LogTemp, Display,
				TEXT("Set in shop to %s"), IsInShop ? TEXT("true") : TEXT("false"));
		}
		else
		{
			UE_LOG(LogTemp, Warning,
				TEXT("Unable to set rich presence: %s"), *Result.Error().ToString());
		}
	});

	Player->PresenceApi().SetRichPresence(Request, SetRichPresenceDelegate);
}


//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::HandleOnAddedToGame(const FString GameInstanceId, const FPragma_GameInstance_ExtAddedToGame Ext)
{
	UE_LOG(LogTemp, Display, TEXT("Game instance ID: %s"), *GameInstanceId );
}

void AMyPlayerController::HandleOnPresenceChanged(const FPragmaPresence& MyPresence)
{
	UE_LOG(LogTemp, Display, TEXT("Presence status changed: "
			"\n Basic: %s, \n Rich: %s, %s"), *UEnum::GetValueAsString<EBasicPresence>(MyPresence.BasicPresence),
		MyPresence.Ext.InShop ? TEXT("In shop") : TEXT("Not in shop"),
		MyPresence.Ext.InParty ? TEXT("In party") : TEXT("Not in party"));
}


//Get functions

//Get player's username (display name and discriminator)
FString AMyPlayerController::GetFullUsername()
{
	UE_LOG(LogTemp, Display, TEXT("Your full username: %s"), *Player->FullDisplayName());
	return Player->FullDisplayName();
}

//Get player's UUID
FString AMyPlayerController::GetPlayerId()
{
	UE_LOG(LogTemp, Display, TEXT("Your player ID: %s"), *Player->Id());
	return Player->Id();
}

FString AMyPlayerController::GetSocialId()
{
	UE_LOG(LogTemp, Display, TEXT("Your social ID: %s"), *Player->SocialId());
	return Player->SocialId();
}


//Get list of player objects for players in the party
TArray<FString> AMyPlayerController::GetPartyPlayers()
{
	
	const TArray<const UPragmaPartyPlayer*> PartyPlayers = Player->GameLoopApi().GetPartyState()->Party()->GetPlayers();
	
	TArray<FString> DisplayNames;

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

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

		FString PlayerId = PartyPlayer->GetPlayerId();

		FString SocialId = PartyPlayer->GetSocialId();

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

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

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

		DisplayNames.Add(DisplayName);

		UE_LOG(LogTemp, Display, TEXT("Display name: %s, Player ID: %s, Social ID: %s"), *DisplayName, *PlayerId, *SocialId);
	}

	return DisplayNames;
}

Next steps #

Congratulations! You now have a custom Unreal implementation of the Pragma Engine Presence service. Continue to the Unreal: Friends tutorial to see how to apply the Friends services in Unreal.