Get into a Game #

The steps in this guide represent a common game loop that takes you from creating a party, through matchmaking, and into a game instance. Specifically, the steps in this guide are:

  1. Create two parties
  2. Add players to the parties
  3. Configure WarmBodyMatchmakingPlugin
  4. Enter both parties into matchmaking
  5. Request game server
  6. Link the game server with the game instance
  7. Connect players to the game server
  8. End a game instance

Step 1: Create parties #

To create a party as a player client, call the Party API CreateParty() method with the following data:

  • ExtCreateRequest: custom party data that the platform needs to know when a party is being created. This data will be stored on the ExtParty payload.
  • ExtPlayerJoinRequest: custom player data. This data will be stored on the ExtPartyPlayer payload.
Player->PartyApi().CreateParty(
  FPragma_Party_ExtCreateRequest{},
  FPragma_Party_ExtPlayerJoinRequest{},
  UPragmaPartyApi::FOnCompleteDelegate::CreateWeakLambda(
    this, [this](const TPragmaResult<> Result)
    {
        if (Result.IsSuccessful())
        {
          UE_LOG(LogTemp, Display,
                  TEXT("Party created."),
                  *Player->PartyApi().GetPartyCache()->Party()->GetId(),
                  *Player->PartyApi().GetPartyCache()->Party()->GetInviteCode());
        }
        else
        {
          UE_LOG(LogTemp, Warning,
                  TEXT("Unable to create party: %s"),
                  *Result.Error().ToString());
        }
    }));
player.PartyApi.CreateParty(
  ExtCreateRequest extCreateRequest, 
  ExtPlayerJoinRequest extPlayerJoinRequest,
  CompleteDelegate onComplete
);

Results:

  • Parties are created, each with a unique invite code
  • The user who created the party is added as party leader

To continue to the next step you’ll need to create at least two parties.

Step 2: Add more players to the parties #

Players can join an existing party using the party’s invite code. For example, a player may want to join a party with an invite code they received from a player via Discord.

As a party leader, obtain the invite code using the Party API’s GetInviteCode() method:

Player->PartyApi().GetPartyCache()->Party()->GetInviteCode();
player.PartyApi.GetPartyCache.GetParty.InviteCode

As a player not in a party, join a specific party using the Party API JoinPartyWithInviteCode() method with the following data:

  • ExtPlayerJoinRequest: Use this payload to specify custom player data, such as a selected champion, to pass to the onAddPlayers method when a player is added to a party. This data will be stored on the ExtPartyPlayer payload
  • inviteCode: invite code obtained by the player joining the party
Player->PartyApi().JoinPartyWithInviteCode(
  const FPragma_Party_ExtPlayerJoinRequest& ExtPlayerJoinRequest,
  const FString& InviteCode,
  const TMap<FString, int>& GameServerZoneToPing,
  const FOnCompleteDelegate& OnComplete
);
player.PartyApi.JoinPartyWithInviteCode(
  ExtPlayerJoinRequest extPlayerJoinRequest,
  string inviteCode,
  Dictionary<string, int> gameServerZoneToPing,
  CompleteDelegate onComplete
);

Result:

  • Player is added to the party

To continue to the next step you’ll need to have at least five players in each party.

Step 3: Configure Matchmaking Plugin #

Matchmaking logic is built in the Matchmaking Plugin. Pragma provides a plugin implementation called WarmBodyMatchmaking, which builds matches with any available parties based on the following configuration values:

Number of teams: How many teams are required to form a match Players per team: How many players are required to form a team

In our example, the WarmBodyMatchmaking plugin attempts to make two teams of five players each.

Enable the WarmBodyMatchmaking plugin and configure the team and player values in you local-dev.yml file:

game:
  pluginConfigs:
    MatchmakingService.matchmakingPlugin:
      class: "pragma.matchmaking.WarmBodyMatchmakingPlugin"
      config:
        numberOfTeams: 2
        playersPerTeam: 5

Result: The WarmBodyMatchmaking plugin is set to create a new game instance when two parties of five players are in matchmaking together.

Step 4: Enter matchmaking #

Party leaders can send their party to the Matchmaking service using the Party API EnterMatchmaking() method. For a party to enter matchmaking, all players in the party must have their ready property set to true. By default this value is false.

Ready up #

Call the Party API SetPartyPlayerReady() method for each player:

Player->PartyApi().SetPartyPlayerReady(
  bool IsReady,
  const FOnCompleteDelegate& OnComplete
);
player.PartyApi.SetPartyPlayerReady(
  bool isReady,
  CompleteDelegate CompleteDelegate
);

Enter matchmaking #

To enter a party of ready players into matchmaking, call the Party API EnterMatchmaking() method:

Player->PartyApi().EnterMatchmaking(
  const FOnCompleteDelegate& FOnCompleteDelegate
);
player.PartyApi.EnterMatchmaking(
  CompleteDelegate CompleteDelegate
);

You can also set a player’s ready state programmatically, such as when the player selects a character.

Result:

  • Each party is placed in a new Matchable object, which joins the default matchmaking queue.
  • The WarmBodyMatchmaking plugin detects a match and creates a NewGameInstance containing the parties.
  • Game server allocation begins.

Step 5: Request game server for game instance #

By default, game server allocation begins as soon as a game instance is created. For local development, use the Pragma-provided LocalProcessGameServerProviderPlugin, which will run a game server executable on the local machine.

Edit the GameInstanceService.gameServerProviderPlugin block of your local-dev.yml file to include a path to your locally built game server.

game:
  pluginConfigs:
    GameInstanceService.gameServerProviderPlugin:
      class: "pragma.gameinstance.LocalProcessGameServerProviderPlugin"
      config:
        serverExecutablePath: "C:/path/to/game/server/executable.exe"
        serverLogDirectory: "."

Result: Pragma is set up to use your local game server executable.

The game server attempts to connect to the Pragma backend with the Connect() method. Once the game server connected, it can request game start data from Pragma by calling the Match API RequestStartGame() function:

Server->Connect(
	Pragma::FServer::FConnectedDelegate::CreateLambda(
		[this, GameInstanceId](const TPragmaResult<>& Result)
		{
			if (Result.IsFailure())
			{
				UE_LOG(LogTemp, Error, TEXT(
					"MyGameServer -- Connect to backend failed."));
				return;
			}

			UE_LOG(LogTemp, Display, TEXT(
				"MyGameServer -- Connect to backend succeeded."));
			Server->MatchApi().RequestStartGame(GameInstanceId);
		}));
server.Connect(result => {
  if (result.IsFailure) {
    Debug.LogError("MyGameServer -- Connect to backend failed.");
    return;
  }

  Debug.Log("MyGameServer -- Connect to backend succeeded.");
  Server->MatchApi().RequestStartGame(GameInstanceId);
});

Result:

  • The game server is connected to the Pragma backend.
  • The game instance is started and game server has the details needed to connect players.

Step 7: Connect players to the game server hosting their game instance #

Once the game server is connected to the Pragma backend and the game instance has started, players can connect to the game server hosting their game. To do so, have the game server call MatchApi.ConnectPlayers() with the following data:

  • GameInstanceId
  • PlayerConnectionDetails
  • Hostname
  • Port
Server->MatchApi().ConnectPlayers(
  GameStartData.GameInstanceId,
  PlayerConnectionDetails,
  Hostname,
  Port,
  UPragmaMatchApi::FOnCompleteDelegate::CreateLambda(
    [this](const TPragmaResult<>& Result)
    {
      if (Result.IsFailure())
      {
        UE_LOG(LogTemp, Error, TEXT("MyGameServer -- Connect players failed."));
        FGenericPlatformMisc::RequestExit(false);
      }
      else
      {
        UE_LOG(LogTemp, Display, TEXT("MyGameServer -- Connect players succeeded."));
      }
    })
);
server.MatchApi.ConnectPlayers(
  GameStartData.GameInstanceId,
  PlayerConnectionDetails,
  Hostname,
  Port,
  result => {
    if (result.IsFailure) {
      Debug.LogError("MyGameServer -- Connect players failed.");
      Application.Quit();
    } else {
      Debug.Log("MyGameServer -- Connect players succeeded.");
    }
  });

Player clients can listen on the OnHostConnectionDetailsReceived event to get connection details such as host name, port, and connection tokens.

Result:

  • Players receive connection information

Step 8: End a game instance from the game server #

Game instances are not ended until the Game Instance interface’s end() method is called. This method can be called directly from any Game Instance plugin implementation, or indirectly by the game server.

To end a game instance as a game server, call the Match API EndGame() method with the following data:

  • GameInstanceId: the game instance to end.
  • PlayerGameResults: custom game instance data (ExtPlayerGameResult) for each player in the game instance.
  • ExtEndGameRequest: custom game instance data sent from the game server.
Server->MatchApi().EndGame(
   GameInstanceId,
   TArray<FPragma_GameInstance_PlayerGameResult>{},
   FPragma_GameInstance_ExtEndGameRequest{},
   UPragmaMatchApi::FOnCompleteDelegate::CreateLambda(
      [this](const TPragmaResult<>& Result)
      {
         if (Result.IsSuccessful())
         {
            UE_LOG(LogTemp, Display, 
                    TEXT("MyGameServer -- End game succeeded."));
         }
         else
         {
            UE_LOG(LogTemp, Error, 
                    TEXT("MyGameServer -- End game failed."));
         }
         FGenericPlatformMisc::RequestExit(false);
      })
);
server.MatchApi.EndGame(
  GameInstanceId,
  new List < PlayerGameResult > (),
  new ExtEndGameRequest(),
  result => {
    if (result.IsSuccessful) {
      Debug.Log("MyGameServer -- End game succeeded.");
    } else {
      Debug.LogError("MyGameServer -- End game failed.");
    }
    Application.Quit();
  });

MatchApi.EndGame() invokes GameInstancePlugin.onEndGame(), which, by default, calls GameInstance.end().

Result:

  • The game instance ends.
  • Players receive the onEndGame event with the ExtGameEnded payload.