Customizing the Instanced Item Plugin #

In this section, we’ll go over writing an Instanced Item Plugin that builds unique gear-type instanced items with custom ext data.

Prerequisites:


Write the Instanced Item Plugin #

  1. Go to 5-ext/ext/src/main/kotlin.
  2. Create a Kotlin file called InstancedItemTutorialPlugin.kt.
  3. Copy the following code into the InstancedItemTutorialPlugin.kt file to create a class that implements the InstancedItemPlugin’s interface:
Even though newInstanced() is the only function utilized, update() must still be implemented in order for the plugin to run.
import kotlin.random.Random
import pragma.content.ContentDataNodeService
import pragma.inventory.InstancedItem
import pragma.inventory.InstancedItemPlugin
import pragma.inventory.InventoryContent
import pragma.inventory.InventoryData
import pragma.inventory.InventoryModifications
import pragma.inventory.InventoryServiceContent
import pragma.inventory.ext.ExtInstancedItem
import pragma.inventory.ext.ExtInstancedItemServerGrant
import pragma.inventory.ext.ExtInstancedItemServerUpdate
import pragma.inventory.ext.ExtInstancedItemUpdate
import pragma.inventory.ext.ExtInstancedSpec
import pragma.inventory.ext.ExtPurchaseRequest
import pragma.inventory.ext.ExtUpdateEntry
import pragma.inventory.ext.Gear
import pragma.inventory.ext.GearSpec
import pragma.services.Service

@Suppress("unused")
class InstancedItemTutorialPlugin(
    val service: Service,
    val contentDataNodeService: ContentDataNodeService
) : InstancedItemPlugin {
    override suspend fun newInstanced(
        instancedSpec: InventoryContent.InstancedSpec,
        inventoryContent: InventoryServiceContent,
        startingInventory: InventoryData,
        pendingInventory: InventoryData,
        clientRequestExt: ExtPurchaseRequest?,
        serverRequestExt: ExtInstancedItemServerGrant?
    ): InstancedItemPlugin.InstancedItemPluginResult {
        val extInstancedItemBuilder = ExtInstancedItem.newBuilder()
        val specExt: ExtInstancedSpec = instancedSpec.ext
        return when (specExt.dataCase) {
            ExtInstancedSpec.DataCase.GEAR_SPEC -> {
                val gearSpec: GearSpec = specExt.gearSpec
                
                // Calculates the attribute value for this piece of gear 
                // based on the content spec min and max.
                val randomAttributeValue = Random.nextInt(
                    gearSpec.primaryAttributeValueMin, 
                    gearSpec.primaryAttributeValueMax
                )
                
                // Builds the proto message that will be persisted and returns it.
                val gearBuilder = Gear.newBuilder()
                    .setAttributeValue(randomAttributeValue)
                    .build()               
                val gearInstance =
                    extInstancedItemBuilder
                        .setGear(gearBuilder)
                        .build()
                InstancedItemPlugin.InstancedItemPluginResult(gearInstance)
            }
            
            else -> {
                error("Unknown item!")
            }
        }
    }

    override suspend fun update(
        initialInstancedItem: InstancedItem,
        instancedSpec: InventoryContent.InstancedSpec,
        updateEntry: InventoryContent.UpdateEntry,
        inventoryContent: InventoryServiceContent,
        startingInventory: InventoryData,
        pendingInventory: InventoryData,
        clientRequestExt: ExtInstancedItemUpdate?,
        serverRequestExt: ExtInstancedItemServerUpdate?
    ): InstancedItemPlugin.InstancedItemPluginResult {
        TODO("Not yet implemented")
    }
}
Method overview

newInstanced() interacts with and builds an instanced item’s ext data with ExtInstancedItem molds. The function does not build or grant new instanced items themselves–it only returns outputs of constructed ExtInstancedItems defined from protos. All custom logic must be defined in the InstancedItemPluginResult.

The extInstancedItemBuilder value uses the public, imported newBuilder() function from the inventory service to build instanced items’ ext content. The specExt value acquires the Instanced Item Specs defined in the ExtInstancedSpec object in inventoryContentExt.proto.

A return when statement returns built ExtInstancedItems by using the specExt value and dataCase (an enum class containing protobuf data) for only instanced item exts that have the gearSpec value. set() methods assign values to a gear’s ext from each item’s gearSpec info in the JSON catalog. All gear item building logic is built with gearBuilder when gearInstanced runs in the InstancedItemPluginResult.

Configure the plugin #

Plugins must be configured in YAML configuration files before they can be utilized by Pragma Engine.

  1. Open 5-ext/config/local-dev.yml.
  2. Register the InstancedItemTutorialPlugin by ensuring the game section of the config file matches the following:
game:
  pluginConfigs:
    InventoryService.instancedItemPlugin:
      class: "InstancedItemTutorialPlugin"

Build plugin changes #

Run the following make command using platform as the working directory to register plugin changes:

make ext

Test the plugin #

In this section, we’ll test if the plugin logic builds unique gear items with custom ext data by running API calls in Postman.

Start Pragma Engine #

Run Pragma Engine via one of the following methods.

Running via Make
Run make run to start the platform. Run this in a terminal with platform as the working directory.
Running in IntelliJ

From the IntelliJ toolbar in the upper right, ensure MainKt - LocalConfigured is selected, then click the play button.

If MainKt - LocalConfigured isn’t available, you will need to configure it. In the IntelliJ toolbar, click the dropdown next to the run button, then click Edit Configurations…. In the Run/Debug Configurations window that appears, expand Kotlin in the left hand side, then select MainKt - LocalConfigured. Click OK. Click the play button in the IntelliJ toolbar to start Pragma Engine.

Once the engine has started successfully, it prints the message [main] INFO main - Pragma server startup complete.

Simulate service calls #

Test the InstancedItemTutorialPlugin by running the required authentication calls and granting three gear-type instanced items to a player. Granting these items causes the InstancedItemSeriesPlugin to run for each item and builds out each item’s ext fields via the InstancedItemPluginResult.

  1. Open Postman.
  2. Navigate to the two service calls PragmaDev ➨ Public ➨ Operator - AuthenticateOrCreateV2 and PragmaDev ➨ Public ➨ Player - AuthenticateOrCreateV2.
  3. Click Send for both service calls and check that the response body for each call has pragmaTokens with a filled pragmaGameToken and pragmaSocialToken.
  4. Open PragmaDev ➨ Game ➨ RPC - Operator ➨ Inventory ➨ GrantItemsOperatorV1 and open the service call’s body.
  5. Insert the following code block to add three instanced grants (metal_sword_1, metal_chest_2, and metal_hat_3) to the test player:
{
    "requestId": 1,
    "type": "InventoryRpc.GrantItemsOperatorV1Request",
    "payload": {
        "playerId": "{{test01PlayerId}}",
        "itemGrants": [
            {
                "instanced": {
                    "ext": {},
                    "catalogId": "metal_sword_1",
                    "tags" : []
                }
            },
            {
                "instanced": {
                    "ext": {},
                    "catalogId": "metal_chest_2",
                    "tags" : []
                }
            },
            {
                "instanced": {
                    "ext": {},
                    "catalogId": "metal_hat_3",
                    "tags" : []
                }
            },
            {
                 "stackable": {
                    "catalogId": "",
                    "amount": 1,
                    "tags": []
                }
            }
        ]
    }
}
  1. Click Send to grant the player their gear.
  2. Confirm the player has their three instanced items by using the call PragmaDev ➨ Game ➨ RPC - Operator ➨ Inventory ➨ GetInventoryOperatorV1.