Integrating the Pragma Server SDK #
Now that we’ve created our initial files, we can start building out the first piece in earnest. We’ll start building out PragmaGameServer with the necessary functionality to communicate with the Pragma Platform. This contains the basic logic for communicating with Pragma Engine.
Setting up the header file #
Open
PragmaGameServer.h
.In your
PragmaGameServer.h
header file, ensure that your includes look like the following:
#include "CoreMinimal.h"
#include "UObject/NoExportTypes.h"
#include "Dto/PragmaGameInstanceRpcDto.h"
#include "PragmaRuntime.h"
#include "PragmaPlayer.h"
#include "PragmaGameServerSubsystem.h"
#include "PragmaGameServer.generated.h"
- Below the includes, add the following:
class UPragmaSession;
UCLASS(Config=Game)
- In the
UPragmaGameServer
class underGENERATED_BODY()
, add the following code:
public:
// Initializes the server and begins broadcasting capacity to the Pragma backend.
void Start();
Building PragmaGameServer functionality #
Now, let’s provide the definition for Start()
.
- Open
PragmaGameServer.cpp
and add the following code:
void UPragmaGameServer::Start()
{
InitConnectionDetails();
auto* World = GetWorld();
checkf(World, TEXT("PragmaGameServer -- Must initialize UPragmaGameServer with an outer object that has a UWorld."));
auto* GameInstance = World->GetGameInstance();
check(GameInstance);
if (!GameInstance->IsDedicatedServerInstance())
{
PRAGMA_LOG(Verbose, "PragmaGameServer -- NOT running because we are not a dedicated server.");
return;
}
PRAGMA_LOG(Log, "PragmaGameServer -- starting...");
Subsystem = GameInstance->GetSubsystem<UPragmaGameServerSubsystem>();
Runtime = Subsystem->Runtime();
Server = Subsystem->Server();
Runtime->Config().PartnerBackendAddress = "http://" + PRAGMA_ADDRESS + ":" + FString::FromInt(PRAGMA_PORT);
Runtime->Config().PartnerSessionSocialAuthToken = SOCIAL_TOKEN;
Runtime->Config().PartnerSessionGameAuthToken = GAME_TOKEN;
Runtime->Server()->Connect(Pragma::FServer::FConnectedDelegate::CreateLambda([this](const TPragmaResult<>& Result)
{
if (Result.IsFailure())
{
PRAGMA_LOG(Log, "Connect failed");
return;
}
ReportCapacity();
}));
if (!FParse::Value(FCommandLine::Get(), *ServerIdSourceCliParam, ServerId) || ServerId.IsEmpty())
{
ServerId = FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens);
PRAGMA_LOG(Error, "PragmaGameServer -- No ServerId specified with configured command line param '%s'. A new ServerId has been generated: %s", *ServerIdSourceCliParam, *ServerId);
}
}
Start()
depends on several helper methods, so let’s define those before we dig into what’s happening. Go back toPragmaGameServer.h
, then add the following under the protected access modifier:
protected:
UPROPERTY()
UPragmaGameServerSubsystem* Subsystem;
Pragma::FServerPtr Server;
Pragma::FRuntimePtr Runtime;
These are variables to hold the Pragma Game Server Subsystem and pointers to the Pragma Server and Runtime objects.
- Also add the following under the private access modifier:
private:
void InitConnectionDetails();
void ReportCapacity();
FString PRAGMA_ADDRESS = "127.0.0.1";
int32 PRAGMA_PORT = 10100;
FString SOCIAL_TOKEN = "";
FString GAME_TOKEN = "";
- Don’t worry about the tokens being empty. We’ll create our tokens in a later step. For now, switch back over to
PragmaGameServer.cpp
and defineInitConnectionDetails()
.
void UPragmaGameServer::InitConnectionDetails()
{
if (!FParse::Value(FCommandLine::Get(), *HostnameCliParam, HostConnectionDetails.Hostname))
{
PRAGMA_LOG(Error, "PragmaGameServer -- Failed to get Hostname from cli parameter '%s' and no hostname was configured in ini. This is likely a problem with backend configuration. Please contact your Pragma representative with this error message.", *HostnameCliParam);
ShutdownServer();
return;
}
checkf(GetWorld(), TEXT("PragmaGameServer -- needs to be created with an outer class that has access to World."));
HostConnectionDetails.Port = GetWorld()->URL.Port;
PRAGMA_LOG(Log, "PragmaGameServer -- Game server is located at: %s:%d", *HostConnectionDetails.Hostname, HostConnectionDetails.Port)
}
InitConnectionDetails()
uses some variables and methods that we haven’t yet defined, so switch back over toPragmaGameServer.h
and add the following:
public:
// Shuts down the server (closes the application).
void ShutdownServer();
private:
FPragma_GameInstance_HostConnectionDetails HostConnectionDetails;
Back in PragmaGameServer.cpp
, define ShutdownServer():
void UPragmaGameServer::ShutdownServer()
{
FGenericPlatformMisc::RequestExit(false);
}
- We haven’t yet defined every helper that our
Start()
method calls, so let’s continue that work. In thecpp
file, provide the following definition forReportCapacity()
:
void UPragmaGameServer::ReportCapacity()
{
FParse::Value(FCommandLine::Get(), TEXT("-PragmaReportCapacityTimeout="), ReportCapacityTimeout);
Server->MatchApi().OnGameStart.AddWeakLambda(this, [this](FPragma_GameInstance_GameStart GameStart)
{
OnGameStartDataReceived.Broadcast(MoveTemp(GameStart));
});
Server->MatchApi().OnGameStartFailed.AddWeakLambda(this,[this](FPragmaError Error)
{
PRAGMA_LOG(Error, "PragmaGameServer -- ReportCapacity failed with error: %s", *Error.ErrorCode());
ShutdownServer();
});
Server->MatchApi().StartReportCapacityPolling(ServerId, GameServerVersion(), FString{}, ReportCapacityTimeout);
}
ReportCapacity()
has references toReportCapacityTimeout
,ServerId
, andGameServerVersion()
, so let’s define those in our header. Switch back toPragmaGameServer.h
and add the following:
protected:
// How long to wait for a game to be allocated before shutting down the server with a failure.
// -1 means it will never time out. Override with -PragmaReportCapacityTimeout=1234.
UPROPERTY(Config)
float ReportCapacityTimeout{10.f};
private:
const FString& GameServerVersion();
FString ServerId;
- Then in
PragmaGameServer.cpp
, defineGameServerVersion()
:
const FString& UPragmaGameServer::GameServerVersion()
{
FParse::Value(FCommandLine::Get(), TEXT("-PragmaGameServerVersion="), TestGameVersion);
return TestGameVersion;
}
This method sets the game server version. In this tutorial, we’ll set the version in the header file. The two methods we just defined use variables that we have not yet defined, so let’s go back to PragmaGameServer.h
and create them:
public:
DECLARE_EVENT_OneParam(UPragmaGameServer, FGameStartDataReceivedEvent, FPragma_GameInstance_GameStart /* GameStartData */)
FGameStartDataReceivedEvent OnGameStartDataReceived;
protected:
UPROPERTY(Config)
FString TestGameVersion{ "GameServerVersion1" };
The game server version can be set via override, commandline parameter, or be hardcoded. Pragma Engine can enforce game server version compatibility; for this tutorial, we’re hardcoding the game server version to GameServerVersion1
. In a later step, we’ll enforce this game server version.
Now that we’ve built all the functions that Start()
depends on, we can now look at what it does. After ensuring that we have the connection details for the game server, it does some Unreal boilerplate, then checks to ensure we are a game server. It then initializes the Pragma SDK and connects to the Pragma Engine backend. Finally, it checks that the game server has been assigned an ID. If no ID has been assigned, it generates one.