Building the Cooking Plugins #
In this section, we’ll build the plugins for cooking.
- Navigate to
platform/5-ext/ext/src/main/kotlin/
in IntelliJ’s Project view. - (if necessary) If the icon for the
kotlin
folder isn’t blue, right click it and select Mark directory as ➨ Sources root. - Right click the
kotlin
folder and chose New ➨ Package. Name itcooking
. - In the
cooking
folder, choose New ➨ Kotlin class/file and name itCookingCrafter.kt
. Replace the contents with the following code:
Due to known issues with IntelliJ performing type inferences on protos, using a value nested in anext
field can cause false syntax error. Create a local variable with a manually assigned type to hold protoext
fields, as we’ve done withincompleteFoodExt
below.
- Create another
kotlin
file in this directory namedDemoCraftingPlugin.kt
. Replace the contents with the following code:
Due to known issues with IntelliJ performing type inferences on protos, using a value nested in anext
field can cause false syntax error. Create a local variable with a manually assigned type to hold protoext
fields, as we’ve done withextCraftingRequirements
below.
package cooking
import pragma.inventory.*
import pragma.inventory.ext.CookingCompleteRequirements
import pragma.inventory.ext.CookingCompleteSpec
import pragma.inventory.ext.CookingSpec
import pragma.inventory.ext.ExtInstancedItem
import pragma.inventory.ext.ExtInstancedItemServerGrant
import pragma.inventory.ext.IncompleteFood
import pragma.utils.TimeProxy
class CookingCrafter(val timeProxy: TimeProxy) {
fun meetsRequirements(
cookingCompleteRequirements: CookingCompleteRequirements,
destroyedInstancedItems: List<InstancedItem>,
): List<String> {
val errors = mutableListOf<String>()
try {
val incompleteFood = destroyedInstancedItems.single()
val incompleteFoodExt: ExtInstancedItem = incompleteFood.ext
val completeTimeMillis =
incompleteFoodExt.incompleteFood.timestampMillis
if (timeProxy.currentEpochMillis() < completeTimeMillis) {
errors.add("Food not cooked yet.")
} else if (
destroyedInstancedItems.single().catalogId !=
cookingCompleteRequirements.consumedCatalogId
) {
errors.add("Cooking Complete Requirement not met.")
}
} catch (e: RuntimeException) {
errors.add(
"Not the correct number of IncompleteFoods were submitted for cooking."
)
}
return errors
}
fun startCooking(cookingSpec: CookingSpec): CraftingPlugin.CraftResult {
val catalogId = cookingSpec.catalogIdToCreate
val readyTime =
cookingSpec.cookTimeInSeconds * 1000 +
timeProxy.currentEpochMillis()
val incompleteFood =
IncompleteFood.newBuilder().setTimestampMillis(readyTime)
return CraftingPlugin.CraftResult(
instancedItemServerGrants =
listOf(
InstancedItemServerGrant(
ExtInstancedItemServerGrant.newBuilder()
.setIncompleteFood(incompleteFood)
.build(),
catalogId,
listOf()
)
),
stackableItemGrants = listOf()
)
}
fun completeCooking(
cookingCompleteSpec: CookingCompleteSpec,
inventoryContent: InventoryServiceContent
): CraftingPlugin.CraftResult {
val instancedList = mutableListOf<InstancedItemServerGrant>()
val stackableList = mutableListOf<StackableItemGrant>()
if (
inventoryContent.instancedSpecs.contains(
cookingCompleteSpec.catalogIdToCreate
)
) {
instancedList.add(
InstancedItemServerGrant(
ExtInstancedItemServerGrant.getDefaultInstance(),
cookingCompleteSpec.catalogIdToCreate,
listOf()
)
)
} else {
stackableList.add(
StackableItemGrant(
cookingCompleteSpec.catalogIdToCreate,
1,
listOf()
)
)
}
return CraftingPlugin.CraftResult(instancedList, stackableList)
}
}
The DemoCraftingPluginclass
sits between Pragma Engine and the CookingCrafter
class.
Pragma Engine calls DemoCraftingPlugin
’s methods, then DemoCraftingPlugin
calls the correct CookingCrafter
methods.
Note: While not demonstrated in this example, the DemoCraftingPlugin
class is also necessary for games that contain multiple types of crafting which are implemented in different classes.
- Create another kotlin file in this directory named
DemoInstancedItemPlugin.kt
. Replace the contents with the following code:
package cooking
import pragma.inventory.*
import pragma.content.ContentDataNodeService
import pragma.inventory.ext.ExtInstancedItem
import pragma.inventory.ext.ExtInstancedItemUpdate
import pragma.inventory.ext.ExtInstancedItemServerGrant
import pragma.inventory.ext.ExtInstancedItemServerUpdate
import pragma.inventory.ext.ExtPurchaseRequest
import pragma.services.Service
class DemoInstancedItemPlugin(val service: Service, val contentDataNodeService: ContentDataNodeService) :
InstancedItemPlugin {
override fun newInstanced(
instancedSpec: InventoryContent.InstancedSpec,
inventoryContent: InventoryServiceContent,
startingInventory: InventoryData,
pendingInventory: InventoryData,
clientRequestExt: ExtPurchaseRequest?,
serverRequestExt: ExtInstancedItemServerGrant?
): InstancedItemPlugin.InstancedItemPluginResult {
if (clientRequestExt != null) {
return InstancedItemPlugin.InstancedItemPluginResult(
extInstancedItem = newClientItem()
)
}
return InstancedItemPlugin.InstancedItemPluginResult(
extInstancedItem = newServerItem(serverRequestExt!!)
)
}
override fun update(
initialInstancedItem: InstancedItem,
instancedSpec: InventoryContent.InstancedSpec,
updateEntry: InventoryContent.UpdateEntry,
inventoryContent: InventoryServiceContent,
startingInventory: InventoryData,
pendingInventory: InventoryData,
clientRequestExt: ExtInstancedItemUpdate?,
serverRequestExt: ExtInstancedItemServerUpdate?
): InstancedItemPlugin.InstancedItemPluginResult {
return InstancedItemPlugin.InstancedItemPluginResult(
ExtInstancedItem.getDefaultInstance()
)
}
private fun newClientItem(): ExtInstancedItem {
val builder = ExtInstancedItem.newBuilder()
return builder.build()
}
private fun newServerItem(
requestExt: ExtInstancedItemServerGrant
): ExtInstancedItem {
val builder = ExtInstancedItem.newBuilder()
if (requestExt.hasIncompleteFood()) {
builder.incompleteFoodBuilder.timestampMillis = requestExt.incompleteFood.timestampMillis
}
return builder.build()
}
}
In this example, we use DemoInstancedItemPlugin
to create our incomplete muffin. Incomplete muffins are instanced items which use the ext
field to store completion time requirements.
- Register your plugins by editing
local-dev.yml
. Ensure thegame
section of the config file contains the followingpluginConfigs
:
game:
pluginConfigs:
InventoryService.craftingPlugin:
class: "cooking.DemoCraftingPlugin"
InventoryService.instancedItemPlugin:
class: "cooking.DemoInstancedItemPlugin"
If the pluginConfigs
section does not exist, you may have to create it.
- Run
make ext
using a terminal from theplatform
directory.