Set Up Parties with Pragma Engine #
This tutorial uses Pragma Engine 0.1.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:
- Define proto enums for party game mode and player character options
- Define party and player functionality with protobufs
- Implement the Party Plugin and override existing functions
- Enable the new plugin
- 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
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/dev.yml
by adding the following configuration:
game:
pluginConfigs:
PartyService.partyPlugin:
class: "party.TutorialPartyPlugin"
5. Set game server version defaults #
Goal #
Ensure your party can enter matchmaking.
Steps #
Pragma Engine checks for server/client compatibility whenever a party changes (players added/removed, party enters matchmaking, etc.). If a mismatch is found when attempting to enter matchmaking, Pragma Engine throws an error and does not add the party to the matchmaking queue.
To ensure your party can enter matchmaking in this tutorial, add the following to your 5-ext/config/local-dev.yml
file:
game:
serviceConfigs:
PartyConfig:
enableGameServerVersionDefault: true
gameServerVersionDefault: "gameServerVersion0Default"
social:
See Manage game server compatibility for more information on server/client mapping.
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.