Upgrading Instanced Items #

In this section, we’ll demonstrate how to upgrade a custom, unique instanced item type called gear with stackable runestone items. To upgrade gear we’ll go over the following steps:

  • Create the updateEntries used to update ext data in gear with specific stackable runestones.
  • Define the catalog of stackable runestone items used to upgrade gear instanced items.
  • Customize an InstancedItemPlugin in Kotlin that can build and upgrade ext data in gear.
  • Test the InstancedItemPlugin, gear, and runestones by running service calls in Postman.

Prerequisites:


Get started #

Run the following commands in the terminal from the platform directory to build the engine and make 5-ext if it doesn’t already exist:

  1. make skip-tests protos engine
  2. make ext

Create the stackable runestones #

One way to upgrade instanced items is by requiring the player to use specific items for the upgrade. Stackable items are great for this purpose, as they are identical to one another like crafting or currency materials.

In this section we’ll create three stackable runestone items used to upgrade gear-type instanced items.

Edit the stackable specs catalog #

Define a runestone_fire, runestone_water, and runestone_earth for upgrading gear-type items.

  1. Go to 5-ext/content/src/StackableSpecs.json.
  2. Add the following code block to create three different types of runestones:
[
  {
    "catalogId": "runestone_fire",
    "name": "Fire Runestone",
    "tags": ["fire", "socketable", "runestones"],
    "limit": 10,
    "removeIfNone": true
  },
  {
    "catalogId": "runestone_water",
    "name": "Ice Runestone",
    "tags": ["water", "socketable", "runestones"],
    "limit": 10,
    "removeIfNone": true
  },
  {
    "catalogId": "runestone_earth",
    "name": "Earth Runestone",
    "tags": ["earth", "socketable", "runestones"],
    "limit": 10,
    "removeIfNone": true
  }
]

Define the update entries #

Update entries catalog different types of item-updates that require some type of cost for client or server based RPCs. ext data for updateEntries can be defined in protos, but the entries themselves are defined in a JSON catalog.

In this section we’ll author the ext fields for three updateEntries in protos and catalog the update entries in a JSON catalog.

Edit the inventory content ext file #

  1. Open 5-ext/ext-protos/src/main/proto/shared/inventoryContentExt.proto.
  2. In the ExtUpdateEntry message, add the following code to define an update entry for upgrading gear with runestones:
message ExtUpdateEntry {
    oneof data {
        string socketed_runestone_catalog_id = 1;
    }
}

Apply proto changes #

Run the following make command using platform as the working directory to apply protobuf changes:

make ext-protos

Create the Update Entries catalog #

  1. Open 5-ext/content/src/UpdateEntries.json.
  2. Add the following code block to define three different UpdateEntries for each runestone:
[
  {
    "id": "socket_runestone_fire",
    "costByCatalogId": {
      "runestone_fire": {
        "cost": 1
      }
    },
    "ext": {
      "socketedRunestoneCatalogId": "runestone_fire"
    }
  },
  {
    "id": "socket_runestone_water",
    "costByCatalogId": {
      "runestone_water": {
        "cost": 1
      }
    },
    "ext": {
      "socketedRunestoneCatalogId": "runestone_water"
    }
  },
  {
    "id": "socket_runestone_earth",
    "costByCatalogId": {
      "runestone_earth": {
        "cost": 1
      }
    },
    "ext": {
      "socketedRunestoneCatalogId": "runestone_earth"
    }
  }
]

Apply content data #

In order to register the content you just defined with Pragma Engine, you must apply your content data changes. You may apply content data either using the command line with make or via an IntelliJ run configuration.

Applying content data using Make

In a terminal with platform as the working directory, run:

make ext-contentdata-apply
Applying content data using IntelliJ
From the IntelliJ toolbar in the upper right, ensure contentdata apply is selected, then click the play button.

Customize the Instanced Item Plugin #

In this section, we’ll customize the Instanced Item Plugin to update ext data in gear-type instanced items using stackable runestones. Make sure you already have logic to build gear in the plugin’s newInstanced() function from the previous tutorial.

  1. Go to 5-ext/ext/src/main/kotlin/InstancedItemTutorialPlugin.kt.
  2. Replace the plugin’s update() function with the following code to enable gear upgrades from stackable runestones:
override fun update(
    initialInstancedItem: InstancedItem,
    instancedSpec: InventoryContent.InstancedSpec,
    updateEntry: InventoryContent.UpdateEntry,
    inventoryContent: InventoryServiceContent,
    startingInventory: InventoryData,
    pendingInventory: InventoryData,
    clientRequestExt: ExtInstancedItemUpdate?,
    serverRequestExt: ExtInstancedItemServerUpdate?
): InstancedItemPlugin.InstancedItemPluginResult { 
    val extInstancedItemBuilder = ExtInstancedItem.newBuilder()
    val extItem: ExtInstancedItem = initialInstancedItem.ext
    return when (extItem.dataCase) {
        ExtInstancedItem.DataCase.GEAR -> {
            val updateExt: ExtUpdateEntry = updateEntry.ext

            //Updates gear with the new attribute value and socketed runestones.
            val gearBuilder = extInstancedItemBuilder.gearBuilder
            gearBuilder.attributeValue = extItem.gear.attributeValue
            gearBuilder.addSocketedRunestones(updateExt.socketedRunestoneCatalogId)

            InstancedItemPlugin.InstancedItemPluginResult(
                extInstancedItemBuilder.setGear(gearBuilder).build(),
                InventoryModifications()
            )
        }

        else -> error("Unknown item!")
    }
} 
Method overview

Writing logic for update() is very similar to writing logic for newInstanced(). The main difference is that update() requires an already existing and built instanced item with ext data in order to function.

  • The extItem value represents the already built exts for gear that players have in their inventory.
  • The updateExt contains each individual UpdateEntry’s ext data defined in the JSON UpdateEntry catalog.

A return when statement returns updated ExtInstancedItems by using the extItem value and dataCase (an enum class containing protobuf data) for instanced item exts that have the gear value. The add() method adds new string values to the SocketedRunestones array in a gear-type item’s ext field. The gearBuilder also uses extItem to grab the original attributevalue from the gear-type item’s ext field. All gear item-building logic is then rebuilt with gearBuilder in the InstancedItemPluginResult.

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 uses runestones to upgrade unique gear items with more 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.

Grant a player three runestones #

  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 in the payload’s itemGrants’s square brackets for runestone_fire, runestone_water, and runestone_earth:
{
  "requestId": 1,
  "type": "InventoryRpc.GrantItemsOperatorV1Request",
  "payload": {
    "playerId": "{{test01PlayerId}}",
    "itemGrants": [
      {
        "stackable": {
          "catalogId": "runestone_fire",
          "amount": 1,
          "tags": []
        }
      },
      {
        "stackable": {
          "catalogId": "runestone_water",
          "amount": 1,
          "tags": []
        }
      },
      {
        "stackable": {
          "catalogId": "runestone_earth",
          "amount": 1,
          "tags": []
        }
      }
    ]
  }
}
  1. Click Send to grant the player their runestones.
  2. Confirm the player has their three instanced items by using the call PragmaDev ➨ Game ➨ RPC - Operator ➨ Inventory ➨ GetInventoryOperatorV1.

Upgrade three gear-type items #

  1. Go to PragmaDev ➨ Game ➨ RPC - Player ➨ Inventory ➨ UpdateItemV4. In the payload’s itemUpdate’s curly brackets, make three separate instanced item update calls by filling the instanceId with each gear’s instanceId (which can be found by running GetInventoryOperatorV1). Then add the correct updateEntryIds for each itemUpdate (check each updateEntryId with your UpdateEntries.json catalog).

  2. Run three separate calls so that the player’s metal_sword_1 has a runestone_fire, metal_chest_2 has a runestone_water, and metal_hat_3 has a runestone_earth.

    Below is an example of what socketing a runestone_fire to a metal_sword_1 looks like:

{
  "payload": {
      "itemUpdate": {
          "instanced":
          {
              "instanceId": "2e9404ab-c970-4e16-9542-8358819846a4",
              "updateEntryId": "socket_runestone_fire",
              "tags": [""]
          }
      }
  }
}
  1. Confirm the player has a metal_sword_1 with a runestone_fire, metal_chest_2 with a runestone_water, and metal_hat_3 with a runestone_earth by using the GetInventoryOperatorV1 call.