Set Up Presence Features with Pragma Engine #

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

The Presence feature allows player clients, game servers, and backend plugins to set a player’s basic presence and a rich presence. The basic presence can be set to “online” or “away”, while the rich presence is fully customizable.

In this tutorial, we’ll edit protobufs and plugin code to allow player clients and backend services to set and update rich presence options. Specifically, we will:

  1. Define functionality that allows:

    • a player client to set a player’s rich presence to in_shop
    • the backend service to set a player’s rich presence to in_party
  2. Create a TutorialPresencePlugin that implements the Pragma Presence Plugin and overrides existing functions to facilitate updating the presence from the player client and backend

  3. Update our TutorialPartyPlugin onAddPlayer() and onRemovePlayer() function to allow the backend to update a player’s in_party value.

Prerequisites

1. Allow players to appear “in shop” and “in party” #

Goal: Allow player client or backend server to set a player’s rich presence.

For our simplified example, we’ll define two true/false presence values: “in shop” and “in party”.

Steps #

In your 5-ext/ext-protos/src/main/proto/shared/presenceExt.proto file:

a. Update the ExtRichPresence message to include all possible presence values:

message ExtRichPresence {
  bool in_shop = 1;
  bool in_party = 2;
}

Now, we want to specify that the player client can update the “in shop” value, and the backend can update the “in party” value.

b. Edit the ExtRichPresencePlayerRequest message as follows:

message ExtRichPresencePlayerRequest {
    oneof update {
      bool in_shop = 1;
    }
}

message ExtRichPresenceBackendRequest {
    oneof update {
      bool in_party = 1;
    }
}

c. 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

2. Implement a Tutorial Presence Plugin #

Goal: Create a new Presence plugin that utilizes the values you defined in the presenceExt.proto file in step 1.

Steps #

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

b. Create a TutorialPresencePlugin class that implements the PresencePlugin interface. The subsequent functions you override will be defined in this class. Make sure to suppress the unused parameters to avoid compile errors.

package presence

import pragma.content.ContentDataNodeService
import pragma.friend.Presence
import pragma.presence.*
import pragma.services.Service
import pragma.utils.GameShardId
import pragma.utils.SocialId

@Suppress("UNUSED_PARAMETER")
class TutorialPresencePlugin(
  service: Service, 
  contentDataNodeService: ContentDataNodeService) : PresencePlugin {

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

c. Override the Presence Plugin’s updateRichPresenceFromPlayer() function. updateRichPresenceFromPlayer() is called when a player client attempts to update their presence. In our simplified example, this method allows the party client to update the “in shop” option.

override suspend fun updateRichPresenceFromPlayer(
    socialId: SocialId,
    gameShardId: GameShardId,
    currentPresence: Presence.Presence?,
    requestExt: ExtRichPresencePlayerRequest,
): ExtRichPresence {

    val extPlayerRich: ExtRichPresence =
        currentPresence?.rich ?: ExtRichPresence.getDefaultInstance()

    return when (requestExt.updateCase) {
        ExtRichPresencePlayerRequest.UpdateCase.IN_SHOP -> {
            extPlayerRich.toBuilder().setInShop(requestExt.inShop).build()
        }
        else -> {
            throw Exception("Invalid update case")
        }
    }
}

d. Override the Presence Plugin’s updateRichPresenceFromBackend() function. updateRichPresenceFromBackend() is called when a backend service or plugin attempts to update a player’s presence. In our simplified example, this method allows the backend to update the “in party” option.

override fun updateRichPresenceFromBackend(
    socialId: SocialId,
    gameShardId: GameShardId,
    currentPresence: Presence.Presence?,
    backendRequestExt: ExtRichPresenceBackendRequest,
): ExtRichPresence {

    val extBackendRich: ExtRichPresence =
        currentPresence?.rich ?: ExtRichPresence.getDefaultInstance()

    return when (backendRequestExt.updateCase) {
        ExtRichPresenceBackendRequest.UpdateCase.IN_PARTY -> {
            extBackendRich.toBuilder().setInParty(backendRequestExt.inParty).build()
        }
        else -> {
            throw Exception("Invalid update case")
        }
    }
}

Next, we’ll allow the backend to automatically update a player’s “in party” presence when the player joins a party.

4. Automatically set players to “in party” #

Goal: Allow the backend to automatically update a player’s “in party” presence when the player joins or leaves a party.

Steps #

a. In your TutorialPartyPlugin, instantiate the presenceClient for use in the PartyPlugin methods:

private val presenceClient: PresenceApi = PresenceApi(service)

b. In your TutorialPartyPlugin, update the onAddPlayer method as follows:

override suspend fun onAddPlayer(
    requestExt: ExtPlayerJoinRequest,
    playerToAdd: Party.PartyPlayer,
    party: Party.Party,
    partyConfig: PartyConfig,
) {
    val extBackendRich: ExtRichPresenceBackendRequest =
        ExtRichPresenceBackendRequest.newBuilder().setInParty(true).build()
    presenceClient.setRichPresence(playerToAdd.socialId, extBackendRich)
}

The onAddPlayer() method is automatically called when a player is added to a party. Our function uses ExtRichPresenceBackendRequest to update the player’s rich presence in_party value to true.

c. In your TutorialPartyPlugin, update the onRemovePlayer() method as follows:

override suspend fun onRemovePlayer(
    party: Party.Party,
    removedPlayer: Party.PartyPlayer,
    removalReason: PartyRpc.RemovalReason
) {
    val extBackendRich: ExtRichPresenceBackendRequest =
        ExtRichPresenceBackendRequest.newBuilder().setInParty(false).build()
    presenceClient.setRichPresence(removedPlayer.socialId, extBackendRich)
}

d. Recompile our 5-ext. Run the following command in a terminal from the platform directory:

make ext

5. Enable the new plugin #

Goal: Enable the new TutorialPresencePlugin.

You need to edit your configuration file so that the engine knows to use the new TutorialPresencePlugin instead of the default Pragma Engine PresencePlugin.

Steps #

Edit your 5-ext/config/local-dev.yml by adding the following configuration:

social:
  pluginConfigs:
    FriendService.presencePlugin:
      class: "presence.TutorialPresencePlugin"

Next steps #

Congratulations! You now have a custom implementation of the Pragma Engine Presence services. Continue to the Unreal: Presence or the Unreal: Friends tutorial to see how to apply the Presence and Friend services in Unreal.