Set Up Parties with Pragma Engine #

This tutorial uses Pragma Engine 0.3.0 to demonstrate how to implement party functionality.

The first part of building out a game flow is managing parties, and the first step in getting started with parties is setting up the party protos and building the Party Plugin.

This tutorial will walk you through defining party features such as game mode using party protos, defining player features such as character selections, and using these characteristics to build an implementation of the Party Plugin. Specifically, you will:

  1. Define proto enums for party game mode and player character options
  2. Define party and player functionality with protobufs
  3. Implement the Party Plugin and override existing functions
  4. Enable the new plugin
  5. Set game server version defaults

As this is a simplified tutorial, we’re completing a basic implementation of the Party Plugin. For more information on protos and plugin implementations, see Plugins and Extension Data

Prerequisites
  • Pragma Engine 0.3.0
  • Associated protos built and a boilerplate 5-ext project ready

1. Define party and player options #

Goal #

Define party game mode and player character values to use in the plugin.

Steps #

In your 5-ext/ext-protos/src/main/proto/shared/partyRpcExt.proto file, create a new proto enum GameMode that defines the party’s available game modes, as well as an enum Character that defines a player’s selectable characters. These values are also used by the Unreal game client as it’s available to the client via the Pragma SDK.

enum GameMode {
	GAME_MODE_UNSPECIFIED = 0;
	CASUAL = 1;
	RANKED = 2;
}
enum Character {
	CHARACTER_UNSPECIFIED = 0;
	KNIGHT = 1;
	MAGE = 2;
	ROGUE = 3;
}
UNSPECIFIED values represent default states when no selections have been made.

2. Define party and player functionality with protobufs #

Goal #

Customize party and player protos to include the game mode and character selection, and allow these values to be updated.

Steps #

a. In the partyRpcExt.proto file, edit the following party and party player messages so they match the code below:

Party messages:

// ext field for party state. This is stored on the party on the backend.
message ExtParty {
  party.GameMode selected_game_mode = 1;
}

// ext field for party details that should be broadcast to all party players
message ExtBroadcastParty {
  party.GameMode selected_game_mode = 1;
}

// request ext field for updating a party selection. This defines what data can be updated.
message ExtUpdatePartyRequest {
  oneof update {
    party.GameMode new_game_mode = 1;
  }
}

Party player messages:

// ext field for player selection state within a party. 
// This is stored on the party player on the backend.
message ExtPartyPlayer {
  party.Character selected_character = 1;
}

// Player-specific data sent to all player clients in the party. 
// If your character selection is public, show it here. 
// For private player data, use ExtPrivatePlayer.
message ExtBroadcastPlayer {
  party.Character selected_character = 1; 
}

// Request ext field for updating a player selection. 
//  This defines what data can be updated.
message ExtUpdatePartyPlayerRequest {
  oneof update {
    party.Character new_character = 1;
  }
}

b. Build the protos so they’re available for use when implementing the Party Plugin by running the following command in the terminal from the platform directory:

make ext-protos

3. Implement the Party Plugin #

Goal #

Create a new plugin called TutorialPartyPlugin that implements the Party Plugin. Override certain functions to utilize the values you defined in the partyRpcExt.proto file in step 2.

Steps #

a. Create a TutorialPartyPlugin.kt Kotlin file in the 5-ext/ext/src/main/kotlin/party/ folder. Create directories as necessary.

b. Create a TutorialPartyPlugin class that implements the PartyPlugin interface (2-pragma/game-common/src/kotlin/pragma/party/PartyPlugin.kt). Make sure to suppress the unused parameters to avoid compile errors.


import pragma.content.ContentDataNodeService
import pragma.matchmaking.ExtMatchmakingKey
import pragma.party.*
import pragma.party.PartyPlugin
import pragma.services.Service

@Suppress("UNUSED_PARAMETER")
class TutorialPartyPlugin(
    val service: Service,
    val contentDataNodeService: ContentDataNodeService
) : PartyPlugin {
    //add custom functions here (steps c-f)
}

c. Override the PartyPlugin’s updateParty function. updateParty is called when a change is made to the party. In our simplified example, this method allows the party’s leader to update the game mode.

override suspend fun updateParty(
    requestingPlayer: Party.PartyPlayer,
    requestExt: ExtUpdatePartyRequest,
    party: Party.Party,
    partyConfig: PartyConfig
) {
    if (!requestingPlayer.isLeader) {
        throw Exception("Non leaders can not change the party settings")
    }

    when (requestExt.updateCase) {
        ExtUpdatePartyRequest.UpdateCase.NEW_GAME_MODE -> {
            val extParty: ExtParty = party.ext
            party.ext =
                extParty.toBuilder().setSelectedGameMode(requestExt.newGameMode).build()
        }
        else -> {
            throw Exception("Invalid update case")
        }
    }
}

d. Override the PartyPlugin’s updatePlayer function. updatePlayer is called when a player makes a change to their own personal selections, such as changing a character, skin, or loadout. In our simplified example, this method allows players to change their character. When a player selects a character, their player state is set to ‘ready’ so they can enter matchmaking.

override suspend fun updatePlayer(
    playerToUpdate: Party.PartyPlayer,
    requestExt: ExtUpdatePartyPlayerRequest,
    party: Party.Party,
    partyConfig: PartyConfig
) {
    when (requestExt.updateCase) {
        ExtUpdatePartyPlayerRequest.UpdateCase.NEW_CHARACTER -> {
            val extPlayer: ExtPartyPlayer = playerToUpdate.ext
            playerToUpdate.ext =
                extPlayer
                    .toBuilder()
                    .setSelectedCharacter(requestExt.newCharacter)
                    .build()
            playerToUpdate.isReady =
                requestExt.newCharacter != Character.CHARACTER_UNSPECIFIED
        }
        else -> {
            throw Exception("Invalid update case")
        }
    }
}

e. Override the PartyPlugin’s the buildExtBroadcastParty function to send the data from within party.ext into the ExtBroadcastParty payload that the plugin function returns:

override suspend fun buildExtBroadcastParty(party: Party.Party): ExtBroadcastParty {
    val partyExt: ExtParty = party.ext
    return ExtBroadcastParty.newBuilder()
        .setSelectedGameMode(partyExt.selectedGameMode)
        .build()
}

f. Override the PartyPlugin’s the buildExtBroadcastPlayer function to send the data from within player.ext into the ExtBroadcastPlayer payload that the plugin function returns:

override suspend fun buildExtBroadcastPlayer(
    party: Party.Party,
    player: Party.PartyPlayer
): ExtBroadcastPlayer {
    val partyPlayerExt: ExtPartyPlayer = player.ext
    return ExtBroadcastPlayer.newBuilder()
        .setSelectedCharacter(partyPlayerExt.selectedCharacter)
        .build()
}

g. Now that we’ve finished creating the party plugin, we need to compile our 5-ext again. Run the following command in a terminal from the platform directory:

make ext

4. Enable the new plugin #

Goal #

Enable the new TutorialPartyPlugin.

Steps #

You need to edit your configuration file so that the engine knows to use the new TutorialPartyPlugin. Edit 5-ext/config/local-dev.yml by adding the following configuration:

game:
  pluginConfigs:
    PartyService.partyPlugin:
      class: "party.TutorialPartyPlugin"

Next steps #

Congratulations! You now have a custom implementation of the Pragma Engine Party Plugin. Continue to the Matchmaking tutorial or see how to apply the Party Plugin in Unreal.