Set Up Parties with Pragma Engine #

This tutorial uses Pragma Engine 0.0.100 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

Prerequisites

  • Have Pragma Engine and its associated protos A format for efficiently serializing structured data that's cross-platform and compatible with several languages. built and have a boilerplate 5-ext project ready

As this is a simplified tutorial, we’re completing a basic implementation of the Party Plugin. See the Party Plugin API reference docs to learn more about other methods. For more information on protos and plugin implementations, see Plugins and Extension Data

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

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

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. The subsequent functions you override will be defined in this class.

class TutorialPartyPlugin(
    val service: Service,
    val contentDataNodeService: ContentDataNodeService,
) : PartyPlugin {

    //add custom functions here (steps c-f)
}

c. Override the Party Plugin’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 Party Plugin’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 Party Plugin’s 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 Party Plugin’s 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 #

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

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

Continue on #

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.