Calling with Custom Plugins #

In advanced cases, custom services may need to be called within other Pragma Engine RPC flows. We’ve provided a set of configurable plugins that can be customized and overridden to add in your functionality.

Plugins are classes that can be enabled via configuration. When enabled, they provide an opportunity to inject custom code into existing Pragma Engine services.

You can call your custom service via a service-to-service RPC call from within a plugin.

Copy a relevant provided plugin from platform/2-pragma/game-common/src/main/kotlin/pragma/ and place it in your custom service directory to preserve existing behavior. To add your custom service, add a new dependent job line to the listOf().

MyCustom example

When a game instance ends, basic functionality can grant rewards or items on completion. For the MyCustom example, we’ll use the Game Instance Plugin’s onEndGame method to add additional game-end processes.

  1. Create a file called MyCustomGameInstancePlugin.kt in the 5-ext/ext/src/main/kotlin/myproject/gameinstance/ directory.
  2. Replicate the contents of the GameInstancePlugin (located in the platform/2-pragma/game-common/src/main/kotlin/pragma/gameinstance/ directory).
  3. In the onEndGame method, add the MyCustomEndGameDependentJob to the listOf to add the MyCustom service to the GameInstancePlugin.
package myproject.gameinstance
class MyCustomGameInstancePlugin(
  val service: Service, 
  val contentDataNodeService: ContentDataNodeService
  ) : GameInstancePlugin {
...
  override suspend fun onEndGame(
      gameInstance: GameInstance.GameInstance,
      playerGameResults: List<PlayerGameResult>,
      requestExt: ExtEndGameRequest
  ): Map<PlayerId, ExtGameEnded> {
      return playerGameResults.associate {
          val extBuilder = ExtGameEnded.newBuilder()
          val timeoutInMillis = 10_000L
          val endGameJobsForPlayer: List<DependentJob<ExtGameEnded.Builder>> = listOf(
              MyCustomEndGameDependentJob(it, service),
              // Other custom dependent jobs
          )

          runAsDependentJobs(service.metricsManager(), logger, "onEndGame") { dependentJobRunner ->
              dependentJobRunner.launchJobs(endGameJobsForPlayer, extBuilder, timeoutInMillis)
          }

          it.playerId to extBuilder.build()
      }
  }
}
  1. Override default plugin behavior by specifying your plugin in your YAML config file. The following config will override the GameInstancePlugin with our newer version that contains the MyCustom service.
  pluginConfigs:
    GameInstanceService.gameInstancePlugin:
      class: "mycustom.gameinstance.MyCustomGameInstancePlugin"
Classes referenced in the config need to have their fully qualified name provided.

Service-to-Service RPC #

Using the SERVICE session within a plugin enables communication with other services. This requires building a custom RPC endpoint using the SERVICE session, which needs its own request/response protos.

MyCustom example

The RPC endpoint should look something like this:

val result = service.requestRpc(
    MyCustomRpc.MyCustomActionServiceV1Request.newBuilder()
        .setCustomId(customId)
        .setCustomData(customData)
        .build(),
    MyCustomRpc.MyCustomActionServiceV1Response::class
)

result.onSuccess { response -> logger.info("MyCustomAction returned ${response} successfully.") }
result.onFailure { serviceError -> logger.error("Something went wrong with MyCustomAction: $serviceError") }

Dependent jobs #

The dependent job pattern handles interactions between multiple interdependent RPC service calls.

There are two key pieces: dependenciesMet and makeRequest.

dependenciesMet is called before running this dependent job to determine if prior RPC calls this job depends on have completed.

makeRequest is called when all required dependencies have been found on the context. Within this function you can perform calculations, business logic or send off requests to other services and await their response.

MyCustom example

The MyCustomGameInstancePlugin can trigger multiple interdependent RPC service calls. The MyCustomGameInstanceDependentJob handles these interactions.

package myproject.gameinstance

class MyCustomGameInstanceDependentJob(
    playerGameResult: PlayerGameResult,
    val service: Service,
) : DependentJob(<ExtGameEnded.Builder>) {
    override fun dependenciesMet(context: DependentJobContext): Boolean {
        return true
    }

    override suspend fun makeRequest(
        context: DependentJobContext,
        mutable: ThreadsafeMutable<ExtGameEnded.Builder>
    ) {
        val ext: ExtPlayerGameResult = playerGameResult.ext
         ...
    }
}