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.
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.
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 Presence function (required)
#
Implement a InitializePresence() function that initializes the Presence service and sets the player’s basic presence to Online. You can also choose to implement the following functionality into a general Initialize() function that also initializes the Party, Game Instance, and Friend services (see the relevant tutorials for examples).
Define InitializePresence() in your MyPlayerController.cpp file:
voidAMyPlayerController::InitializePresence(){UPragmaPresenceApi::FOnCompleteDelegatePresenceInitializeDelegate;PresenceInitializeDelegate.BindWeakLambda(this,[this](constTPragmaResult<>&Result){if(Result.IsSuccessful()){UE_LOG(LogTemp,Display,TEXT("Presence service initialized."));}else{UE_LOG(LogTemp,Warning,TEXT("Presence service failed to initialize: %s"),*Result.Error().ToString());}});Player->PresenceApi().Initialize(PresenceInitializeDelegate);}
To test this functionality using the Unreal in-game console, issue InitializePresence as each player client (e.g. test01 and test02).
To apply this functionality using Unreal Blueprints, you can call the InitializePresence() function as part of another operation, such as upon login.
When the service is successfully initialized, you should see “Presence service 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).
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”.
Define SetPresenceToOnline() and SetPresenceToAway() in your MyPlayerController.cpp file:
voidAMyPlayerController::SetPresenceToOnline(){Player->PresenceApi().SetAsOnline(UPragmaPresenceApi::FOnCompleteDelegate::CreateWeakLambda(this,[](constTPragmaResult<>&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."));}}));}voidAMyPlayerController::SetPresenceToAway(){Player->PresenceApi().SetAsAway(UPragmaPresenceApi::FOnCompleteDelegate::CreateWeakLambda(this,[](constTPragmaResult<>&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”.
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).
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:
Define SetInShop() in your MyPlayerController.cpp file:
voidAMyPlayerController::SetInShop(constboolIsInShop){FPragma_Presence_ExtRichPresencePlayerRequestRequest;Request.Update.SetInShop(IsInShop);UPragmaPresenceApi::FOnCompleteDelegateSetRichPresenceDelegate;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.
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).
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.
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).
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.
#pragma once
#include"CoreMinimal.h"#include"GameFramework/PlayerController.h"#include"PragmaPtr.h"#include"PragmaGameInstanceSubsystem.h"#include"PragmaResult.h"#include"Dto/PragmaPartyRpcExtDto.h"#include"Services/Party/PragmaPartyInvite.h"#include"MyPlayerController.generated.h"usingFFriendList=TMap<FString,FPragmaFriend>;// Forward declares Pragma pointer types for Pragma::FPlayer.
PRAGMA_FWD(FPlayer);UCLASS()classUNREALTUTORIAL_APIAMyPlayerController:publicAPlayerController{GENERATED_BODY()public:virtualvoidBeginPlay()override;// Weak pointer to our Pragma Player owned by the PragmaLocalPlayerSubsystem.
Pragma::FRuntimePtrRuntime;Pragma::FPlayerPtrPlayer;//Login and Logout functions
UFUNCTION(Exec,BlueprintCallable,Category="Pragma")voidLogIn(constFString&Username);UFUNCTION(Exec,BlueprintCallable,meta=(Category="Pragma"))voidLogOut();//Initialization functions
UFUNCTION(Exec,BlueprintCallable,Category="Pragma")voidInitializeServices();UFUNCTION(Exec,BlueprintCallable,Category="Pragma")voidInitializeParty();UFUNCTION(Exec,BlueprintCallable,Category="Pragma")voidInitializeGameInstance();UFUNCTION(Exec,BlueprintCallable,Category="Pragma")voidInitializePresence();//Party functions
UFUNCTION(Exec,BlueprintCallable,Category="Pragma")voidCreateParty();UFUNCTION(Exec,BlueprintCallable,Category="Pragma")voidJoinPartyWithInviteCode(constFString&InviteCode);UFUNCTION(Exec,BlueprintCallable,Category="Pragma")voidJoinPartyWithPartyId(constFString&PartyId);UFUNCTION(Exec,BlueprintCallable,Category="Pragma")voidSendPartyInviteByPlayerId(constFString&InviteeId);UFUNCTION(Exec,BlueprintCallable,meta=(Category="Pragma"))voidRespondToPartyInvite(constFString&PartyInviteId,constboolresponse);UFUNCTION(Exec,BlueprintCallable,Category="Pragma")voidSetGameMode(constEPragma_Party_GameMode&GameModeSelection);UFUNCTION(Exec,BlueprintCallable,meta=(Category="Pragma"))voidSetCharacter(constEPragma_Party_Character&CharacterSelection);UFUNCTION(Exec,BlueprintCallable,meta=(Category="Pragma"))voidLeaveParty();//Matchmaking functions
UFUNCTION(Exec,BlueprintCallable,meta=(Category="Pragma"))voidEnterMatchmaking();//Presence functions
UFUNCTION(Exec,BlueprintCallable,Category="Pragma")voidSetPresenceToOnline();UFUNCTION(Exec,BlueprintCallable,Category="Pragma")voidSetPresenceToAway();UFUNCTION(Exec,BlueprintCallable,Category="Pragma")voidSetInShop(constboolIsInShop);private:voidHandleLoggedIn(constTPragmaResult<>&Result);voidHandleLoggedOut();voidHandleOnPartyInviteReceived(constFPragmaPartyInvite&MyPartyInvite);voidHandleOnAddedToGameInstance(constFPragmaGameInstance&MyGameInstance);voidHandleOnPresenceChanged(constFPragmaPresence&MyPresence);};
Sample MyPlayerController.cpp source file
#include"MyPlayerController.h"#include"PragmaPlayer.h"#include"Dto/PragmaAccountRpcDto.h"#include"Dto/PragmaPartyRpcExtDto.h"#include"Pragma/Api/Player/PragmaFriendApi.h"#include"Services/Party/PragmaPartyInvite.h"#include"Pragma/Common/Friend/PragmaPresence.h"#include"Pragma/Api/Player/PragmaPresenceApi.h"#include"PragmaLocalPlayerSubsystem.h"voidAMyPlayerController::BeginPlay(){Super::BeginPlay();UE_LOG(LogTemp,Display,TEXT("Initializing"));constauto*Subsystem=GetLocalPlayer()->GetSubsystem<UPragmaLocalPlayerSubsystem>();// The UPragmaLocalPlayerSubsystem is automatically initialized
// with a Pragma Player object for every LocalPlayer.
Player=Subsystem->Player();Player->PartyApi().OnPartyInviteReceived.AddUObject(this,&AMyPlayerController::HandleOnPartyInviteReceived);Player->GameInstanceApi().OnAddedToGameInstance.AddUObject(this,&AMyPlayerController::HandleOnAddedToGameInstance);}//Login and Logout functions
voidAMyPlayerController::LogIn(constFString&Username){Player->LogIn(EPragma_Account_IdProvider::UNSAFE,Username,Pragma::FPlayer::FLoggedInDelegate::CreateUObject(this,&AMyPlayerController::HandleLoggedIn));}voidAMyPlayerController::LogOut(){Player->LogOut(Pragma::FPlayer::FLoggedOutDelegate::CreateUObject(this,&AMyPlayerController::HandleLoggedOut));}//Initialization functions
voidAMyPlayerController::InitializeParty(){UPragmaPartyApi::FOnCompleteDelegatePartyInitializeDelegate;PartyInitializeDelegate.BindWeakLambda(this,[this](constTPragmaResult<>&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);}voidAMyPlayerController::InitializeGameInstance(){UPragmaGameLoopApi::FOnCompleteDelegateGameInstanceInitializeDelegate;GameInstanceInitializeDelegate.BindWeakLambda(this,[this](constTPragmaResult<>&Result){if(Result.IsSuccessful()){UE_LOG(LogTemp,Display,TEXT("Game Instance service initialized."));}else{UE_LOG(LogTemp,Warning,TEXT("Game Instance service failed to initialize: %s"),*Result.Error().ToString());}});Player->GameInstanceApi().Initialize(GameInstanceInitializeDelegate);}voidAMyPlayerController::InitializePresence(){UPragmaPresenceApi::FOnCompleteDelegatePresenceInitializeDelegate;PresenceInitializeDelegate.BindWeakLambda(this,[this](constTPragmaResult<>&Result){if(Result.IsSuccessful()){UE_LOG(LogTemp,Display,TEXT("Presence service initialized."));}else{UE_LOG(LogTemp,Warning,TEXT("Presence service failed to initialize: %s"),*Result.Error().ToString());}});Player->PresenceApi().Initialize(PresenceInitializeDelegate);}//Party functions
voidAMyPlayerController::CreateParty(){UPragmaPartyApi::FOnCompleteDelegateOnPartyCreatedDelegate;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);}voidAMyPlayerController::JoinPartyWithInviteCode(constFString&InviteCode){UPragmaPartyApi::FOnCompleteDelegateJoinWithInviteCodeDelegate;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);}voidAMyPlayerController::JoinPartyWithPartyId(constFString&PartyId){UPragmaPartyApi::FOnCompleteDelegateJoinWithPartyIdDelegate;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);}voidAMyPlayerController::SendPartyInviteByPlayerId(constFString&InviteeId){UPragmaPartyApi::FOnInviteSentDelegateSendPartyInviteDelegate;SendPartyInviteDelegate.BindWeakLambda(this,[this,InviteeId](constTPragmaResult<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);}voidAMyPlayerController::RespondToPartyInvite(constFString&PartyInviteId,constboolResponse){UPragmaPartyApi::FOnCompleteDelegateRespondInviteDelegate;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);}voidAMyPlayerController::SetGameMode(constEPragma_Party_GameMode&GameModeSelection){if(Player->PartyApi().GetPartyCache()->Party()->IsLeader(Player->Id())==true){FPragma_Party_ExtUpdatePartyRequestRequest;Request.Update.SetNewGameMode(GameModeSelection);UPragmaPartyApi::FOnCompleteDelegateUpdatePartyDelegate;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"));}}voidAMyPlayerController::SetCharacter(constEPragma_Party_Character&CharacterSelection){UPragmaPartyApi::FOnCompleteDelegateUpdatePartyPlayerDelegate;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_ExtUpdatePartyPlayerRequestRequest;Request.Update.SetNewCharacter(CharacterSelection);Player->PartyApi().UpdatePartyPlayer(Request,UpdatePartyPlayerDelegate);}voidAMyPlayerController::LeaveParty(){UPragmaGameLoopApi::FOnCompleteDelegateLeavePartyDelegate;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);}//Matchmaking functions
voidAMyPlayerController::EnterMatchmaking(){UPragmaPartyApi::FOnCompleteDelegateOnEnterMatchmakingDelegate;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->PartyApi().EnterMatchmaking(OnEnterMatchmakingDelegate);}//Presence functions
voidAMyPlayerController::SetPresenceToOnline(){Player->PresenceApi().SetAsOnline(UPragmaPresenceApi::FOnCompleteDelegate::CreateWeakLambda(this,[](constTPragmaResult<>&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."));}}));}voidAMyPlayerController::SetPresenceToAway(){Player->PresenceApi().SetAsAway(UPragmaPresenceApi::FOnCompleteDelegate::CreateWeakLambda(this,[](constTPragmaResult<>&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."));}}));}voidAMyPlayerController::SetInShop(constboolIsInShop){FPragma_Presence_ExtRichPresencePlayerRequestRequest;Request.Update.SetInShop(IsInShop);UPragmaPresenceApi::FOnCompleteDelegateSetRichPresenceDelegate;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
voidAMyPlayerController::HandleLoggedIn(constTPragmaResult<>&Result){if(Result.IsFailure()){UE_LOG(LogTemp,Display,TEXT("Pragma -- Login failed: %s"),*Result.ErrorCode());}UE_LOG(LogTemp,Display,TEXT("Pragma -- Logged in."));}voidAMyPlayerController::HandleLoggedOut(){UE_LOG(LogTemp,Display,TEXT("Pragma -- Logged out."));}voidAMyPlayerController::HandleOnPartyInviteReceived(constFPragmaPartyInvite&MyPartyInvite){UE_LOG(LogTemp,Display,TEXT("Party invite received. Respond using invite ID: %s"),*MyPartyInvite.GetInviteId());}voidAMyPlayerController::HandleOnAddedToGameInstance(constFPragmaGameInstance&MyGameInstance){UE_LOG(LogTemp,Display,TEXT("Game instance ID: %s"),*MyGameInstance.GetId());}voidAMyPlayerController::HandleOnPresenceChanged(constFPragmaPresence&MyPresence){UE_LOG(LogTemp,Display,TEXT("Presence status changed: ""\n Basic: %s, \n Rich: %s, %s"),*UEnum::GetValueAsString<EBasicPresence>(MyPresence.GetBasicPresence()),MyPresence.GetExt().InShop?TEXT("In shop"):TEXT("Not in shop"),MyPresence.GetExt().InParty?TEXT("In party"):TEXT("Not in party"));}
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.
We use cookies to analyze our traffic. We also share information about your use of our site with our analytics partners who may combine it with other information that you’ve provided to them or that they’ve collected from your use of their services.