Skip to content

In Game Marketplace

This tutorial describes how to create a simple in-game marketplace using the Cloud Code and Collection Data features of ChilliConnect. The marketplace will allow players to:

  • Offer items in their inventory for sale
  • Browse and search for available sale items
  • Purchase an item from another player that is on sale

A Cloud Data Collection will be used to store and search items that have been put up for sale and make them visible to other players of the game. Cloud Code will be used to purchase Sale Items in a server authoritative fashion, handling the transfer of the item and in game currency. For this tutorial we'll use the API Explorer to quickly test the marketplace as we develop it without needing to integrate an SDK.

Creating the Collection

The first step is to create a new Collection that will store items that have been offered for sale. From the ChilliConnect dashboard, either select an existing game or create a new game. From the left hand menu, select the "Connect" option, then the "Collections" option. Press the "Add Collection" button to create a new Collection. Enter "Sale Items" as the Collection name and SALE_ITEMS as the Key. For the Sale Items collection, we'll specify the following fields:

ItemID The item from their inventory that the player is offering for sale.
Description A description of the item that the player has provided.
Name The name of the item being offered.
Price The price the player is offering the item for
Completed A boolean that indicates whether the sale has been completed
PurchasedBy The ChilliConnectID of the player that purchased the item

We define ItemID, PurchasedBy and Price as indexes as we'll query the collection using these fields.

Setting Permissions

Once the Collection has been created, the next step is to modify the permissions that determine what players can read or write objects within the Collection. Select the "Permissions" tab from the Collection view then "Edit Permissions".

By default, through direct ChilliConnect API calls UpdateCollectionObject and DeleteCollectionObject, each object within the Collection can only be modified by the initial player that created it, but can be read using QueryCollection by all players. When accessing a collection from a Cloud Code script, all players can read and write any object.

For the marketplace system, we are going to strictly control modifications to Sale Items through Cloud Code; we don't want items to be directly modified by the game client. To have ChilliConnect automatically enforce this, change the API Write permission to "None" and press the "Edit Permissions" button to save the changes.

Defining the Economy

In order to have some items to sell and currency with which to purchase them, we'll create some Catalog definitions. Go the the Catalog section of your dashboard, under "Connect", "Catalog", and add a Currency named COINS with an initial allocation of 100 and an Inventory item named SWORDS with an initial allocation of 2. This will provide us with some test data when we create player new accounts to test the marketplace system. Your Catalog should look as below:

Adding Sale Items

We will now add a new Cloud Code script that will put items in the player inventory up for sale. This script would be called directly from the game using the RunScript method. From the Dashboard, select the "Cloud Code" option, then add a new script with the following request parameters:

ItemID The item the player wants to put up for sale.
Description A text description that the player can provide of the item.
Name The name of the item being offered.
Price The price in COINS that the player wants to offer the item for.

Press "Add Script" to create the new script then paste the code below in to the editor and press the "Save" button:

try {
var sdk = ChilliConnect.getSdk("2.0.0");

//Extract ItemId from Request
var itemID = ChilliConnect.Request.ItemID;

//Verify that the Item exists in the player's inventory
var items = sdk.Economy.getInventoryForItemIds([itemID]);
if ( items.Items.length === 0 ) {
    return { "Error" : "ItemID does not exist" }
}

//Verify the item has not been put up for sale already
var existingSale = sdk.CloudData.queryCollection( "SALE_ITEMS", 
    "Value.ItemID = :ItemID", [], {"ItemID" : itemID } );

if ( existingSale.Total > 0 ) {
    return { "Error" : "ItemID already for sale" }
}

//Get the Item
var item = items.Items[0];

//Extract Price from request and ensure it's greater than 0
var price = parseInt(ChilliConnect.Request.Price);
if ( price <= 0 ) {
    return { "Error" : "Invalid price" }
}

//Limit the description to a max of 50 characters
var description = ChilliConnect.Request.Description.substring(0, 50);

//Add the item to the sale items collection
var saleItem = sdk.CloudData.addCollectionObject( "SALE_ITEMS", {
    "ItemID" : itemID,
    "Name" : item.Name,
    "Price" : price,
    "Description" : description,
    "Completed": false
});

//Return the ID of the item to the client
return { "SaleItemID" : saleItem.ObjectID };

}
catch(e)
{
    ChilliConnect.Logger.error( e.toString() );

    return { "Error" : "Unknown Error" };
}

The script is fairly self explanatory. We first validate the provided parameters then add a new object to the Sale Items collection using the AddCollectionObject method. We also check that this specific item has not already been offered for sale using QueryCollection - we'll explore the Query method in more detail later in the tutorial.

Creating a Test Sale Item

To test the new script, we need a player account. Open up the API Explorer from your Dashboard. Create and log in with a new player account. As we are going to be referring to the account later, you should supply an Email and Password or take a note of the ChilliConnectID and ChilliConnectSecret.

Still in the API Explorer, once you have logged in, you can run the GetInventory and GetCurrencyBalance methods to confirm the player has been allocated 2 instances of the SWORD item and 100 of the COINS currency. Take a note of one the ItemID values from the player inventory - we'll use this as an input to the Put Item For Sale script.

Return to the script view in the dashboard and navigate to the "Test" tab. This will provide a form that can be used to specify input parameters for executing the script:

From here, enter the ChilliConnectID of the test player you created from the API Explorer, as well as a description, price and the ItemID returned from the GetInventory call. Press the "Run" button to execute the script. In the script output window, you should see response similar to below, confirming that the sale item was added:

{
 "Output": {
  "SaleItemID": "98a4ae8c-c09d-11e6-80d7-08002739d0fc"
 }
}

Going back to the Collection view, the newly created item should been added to the Sale Items Collection:

For testing purposes, we used the Cloud Script test harness to quickly create a sale item. For an actual game, you would typically publish the script before calling the RunScript method from your game. To keep this tutorial simple, we will skip this step.

Searching For Sale Items

Now that we've created an item for sale, we need a way for players to be able to search for available items. As the permission settings for the Sale Items Collection allows all players to read objects in the Collection, we can use the QueryCollection method to find items that match a given criteria. QueryCollection accepts a query string in a simple, easy to understand format.

Using the API Explorer, select the QueryCollection method, set Key to SALE_ITEMS and Value.Completed = false as the Query string.

Press the "Query Collection" button to submit the query. The response panel in the explorer should return a JSON response similar to the below, containing the single Sale Item we added in the previous step:

{
    "Total": 1,
    "Page": 1,
    "PageSize": 20,
    "Objects": [
        {
            "ObjectID": "d1f0b718-bebc-11e6-9146-08002739d0fc",
            "CreatedBy": {
                "ChilliConnectID": "Sp5JBAySgz1j7MK0KPZo48voLv4e7Dom",
                "UserName": null,
                "DisplayName": null
            },
            "DateCreated": "2016-12-01T09:41:26",
            "ModifiedBy": null,
            "DateModified": null,
            "Value": {
                "ItemID": "GDJbn0zCCaM6rbs8lFg7trmJqXFbVmzP",
                "Name": "Sword",
                "Price": 20,
                "Description": "An amazing sword",
                "Completed": false
            }
        }
    ]
}

Although this is a simple example, the Query syntax is highly flexible and can be used to search on any of the defined fields on the Collection. Here are some of the other queries we might want to perform in an actual game:

  • Find all swords that are for sale:
Value.Name = "Sword" AND Value.Completed = false
  • Find all items that cost less than 10 Coins
Value.Price < 10 AND Value.Completed = false
  • Find all items that were either sold or bought by a specific player
CreatedBy = "Sp5JBAySgz1j7MK0KPZo48voLv4e7Dom" OR Value.PurchasedBy = "Sp5JBAySgz1j7MK0KPZo48voLv4e7Dom"

You can try all of these out using the API Explorer. This ability to easily query Collections using any combination of fields is what makes the Collection Data feature so powerful. In this tutorial, we are only covering a handful of the field types and query operators available in Collection Data. For more details, see the full documentation

Purchasing Items

To enable players to purchase an item, we will create another Cloud Code script. Add new Cloud Code script that accepts a single parameter, SaleItemID.

Paste in the below code and save the script:

try {
    var sdk = ChilliConnect.getSdk("2.0.0");

    //Get the item from the Collection
    var collectionObjects = sdk.CloudData.GetCollectionObjects("SALE_ITEMS", ChilliConnect.Request.SaleItemID);
    if ( collectionObjects.Objects.length === 0 ) {
        return { "Error" : "Sale Item Not Found" };
    }

    var saleItem = collectionObjects["Objects"][0];

    //Check the sale has not been completed
    if ( saleItem.Value.Completed ) {
        return { "Error" : "Sale Completed" };
    }

    //Check the player has enough currency to purchase this item
    var coins = sdk.Economy.getCurrencyBalance(["COINS"]).Balances[0];
    if ( coins.Balance < saleItem.Value.Price ) {
        return { "Error" : "Insufficient Balance" };
    }

    //Update the selling player's inventory to remove the item and credit their balance
    var soldItem = sdk.PlayerAccounts.asPlayer(saleItem.CreatedBy.ChilliConnectID, function() {

        //Check the item is still in  the player's inventory
        var items = sdk.Economy.getInventoryForItemIds([saleItem.Value.ItemID]);
        if ( items.Items.length === 0 ) {
            return false;
        }

        //Add to selling player's balance
        sdk.Economy.addCurrencyBalance("COINS", saleItem.Value.Price);

        //Remove sold item from their inventory
        sdk.Economy.removeInventoryItem(saleItem.Value.ItemID);

        return items.Items[0];
    });

    //Return an error if the item was not found
    if ( soldItem === false ) {
        return { "Error" : "Item not found" };
    }

    //Copy the item to the purchasing account
    var newItemID = sdk.Economy.addInventoryItem( soldItem.Key, soldItem.InstanceData );

    //Deduct the price of the item from the player's balance
    var newBalance = sdk.Economy.removeCurrencyBalance("COINS", saleItem.Value.Price);

    //Get the logged in player details for the ChilliConnectID
    var playerDetails = sdk.PlayerAccounts.getPlayerDetails();

    //Update the sale item
    sdk.CloudData.updateCollectionObject(
        "SALE_ITEMS",
        saleItem.ObjectID, 
        { 
            "Completed" : true, 
            "PurchasedBy" : playerDetails.ChilliConnectID
        }
    );

    //Return the new currency balance and the new item
    return {
        "CoinsBalance" : newBalance.Balance,
        "Item" : {
            "ItemID" : newItemID,
            "Key" : soldItem.Key,
            "Name" : soldItem.Name,
            "InstanceData" : soldItem.InstanceData
        }
    };

}
catch(e)
{
    ChilliConnect.Logger.error( e.toString() );

    return { "Error" : "Unknown Error" }
}

This script is more advanced and introduces some new concepts.

We make sure that the Sale Item has not already been purchased by another player before verifying the player has enough currency to make the purchase. In order to carry out the sale, the AsPlayer method is used. AsPlayer will execute a provided callback function within the context of another player account. This allows the script to modify the selling player's inventory and currency balances, removing the sold item and crediting their coin balance with the sale price.

The purchased item is then added to the purchasing player's inventory and coins deducted. The Sale Item is updated to be marked as completed, and the ChilliConnectID of the player that purchased the item also recorded. The details of the newly purchased item are returned along with their updated coin balance.

Note that technically we are actually removing the existing item and adding a new item to the purchasing player's account, rather than transferring the same item. However, since we are also copying the instance data, from a player's perspective the item will appear identical.

Making a Test Purchase

As with the first script, we will create a new player account to test script.

From the API Explorer, create another player account and log in. As before, take a note of the ChilliConnectID. From the script view of the Purchase Item script, select the "Test" tab and enter the ObjectID of the Sale Item created by the first script. This is returned as the SaleItemID property from the script. Alternatively, you can also find the ObjectID by selecting the item from the Sale Items Collection view. Enter the ChilliConnectID of the newly created test account in the Player" field.

Press the "Run" button to execute the script. The below output should be returned:

{
 "Output": {
  "CoinsBalance": 87,
  "Item": {
   "ItemID": {
    "ItemID": "99jZJcgpEmJ4wqn4UWOoQUX6TPrjTZVr",
    "WriteLock": "1"
   },
   "Key": "SWORD",
   "Name": "Sword",
   "InstanceData": null
  }
 }
}

Returning to the API Explorer, you can rerun the GetInventory and GetCurrencyBalance methods to confirm that the player's account has been deducted the relevant amount of COINS and the new SWORD item added to their inventory. Viewing the Sale Item from the Collections view, we can also see that the Completed property has been set to true and the ChilliConnectID of the purchasing player stored in the PurchasedBy field:

Next Steps

This tutorial showed how Cloud Code and Collection Data can be combined to together to quickly create a server authoritative in-game marketplace. Collection Data is highly flexible and can be applied to an almost unlimited number of use cases including teams and clans, turn based multiplayer, challenges, social quests and more. It's also worth remembering that although this example showed how Cloud Code can be used to ensure that all changes to the Collection were server authoritative, this is not required, and you can also integrate Collections directly into your game using the public API.

To further explore Collection Data and Cloud Script, you could:

  • Update the Purchase Item script to send the selling player a message
  • Explore other Collection Storage field and search types, such as Geo Location support
  • Implement other social features using Collection Data and Cloud Code