Get Started

The following sections cover the initial steps to familiarize yourself with GameBus API.

What do I need to know beforehand?

logo gamebus GameBus

First of all try to get all the information you need about the platform.

For more info please see: (https://www.gamebus.eu/)

logo chrome Chrome DevTools

Chrome has a very useful tool for developers (or anyone interested) that makes every HTTP request cycle made by the browser accessible in a very user-friendly way.

logo postman

To get a very good understanding of how the API does its magic requesting actions and getting data as response, Postman acts, let’s say, as your magnifying glass. Not only to see way better what’s going on, but to also easily make HTTP requests, get responses, and save them to be executed how many times you want.

For more info please see: (https://learning.postman.com/)

logo unirest java Unirest-Java

A very easy to use lightweight HTTP client library.

Here’s an example of a GET request using Unirest-Java:

Unirest.get("http://httpbin.org")
        .queryString("fruit", "apple")
        .queryString("droid", "R2D2")
        .asString();

// Results in "http://httpbin.org?fruit=apple&droid=R2D2"

And for a POST:

HttpResponse<JsonNode> response = Unirest.post("http://httpbin.org/post")
        .header("accept", "application/json")
        .queryString("apiKey", "123")
        .field("parameter", "value")
        .field("firstname", "Gary")
        .asJson();

Some parts of this documentation rely on a very basic HTTP client/helper implemented on top of Unirest-Java.

For more info please see: (https://kong.github.io/unirest-java/)

How can I access the platform?

First step to act as a client is to create a GameBus test account via https://app3.gamebus.eu/ in order to have access to the API resources.

This address points to a test server, so make sure to use it as all data stored there is flushed regularly.

The sign-up screen:

gamebus sign up screen

That’s it!

Am I making requests already?

By using the application the user is already making a lot of requests, in other words, using the API. This can be easily seen with the help of Chrome Dev Tools.

To have a look at all the requests that are being made:

  1. Open Chrome Dev Tools with Ctrl+Shift+I

  2. Go to the Network tab

The Network tab displays information of all requests made by the browser. For every action that demands a client-server interaction the application will trigger one or more requests, and by selecting one of them the associated information will be displayed.

chrome dev token 1

Information such as Headers and Response are very useful, and can give a good picture of what kind of request was made behind each action, and possible insights about the related data and resource used to retrieve it.

chrome dev token 2

chrome dev token 3

Ok, fine, but how can I make requests from outside the App?

That’s when Postman comes in handy. But first an authorization token must be known, as it’s going to be used by each created request coming from a client.

The token is a Bearer token in the form of: 71357b62-262d-4fa8-8a5c-ae8314abc5cb, and to access its value, Chrome Dev Tools can be used as well:

  1. Open Chrome Dev Tools with Ctrl+Shift+I

  2. Go to the Application tab

  3. Then: Storage > Local Storage > authuser

  4. Copy the authorization token value (token)

  5. Copy both user and player IDs (uid and pid)

chrome dev token 0

While listing existing activities on the application, one can note that the request used to retrieve and list existing activities is similar to:

{{api.url}}/players/{{player.id}}/activities

and that a real player.id value is there, leading to something like:

{{api.url}}/players/123/activities

Now on Postman a GET request can be made like:

collection players get all activities sample

where {{api.url}} must be replaced by its real value, and both {{player.id}} and {{auth.token}} by the previously copied values for authorization token and player ID, respectively. By sending the request a response similar to the one displayed by Chrome Dev Tools will be received.

It is important to mention that the previous request is part of the Postman collection included in this Wiki, as well as the mentioned variables ({{api.url}}, {{player.id}}, {{auth.token}}) that are part of the also included Postman environment. Please use the links bellow to download both:

Postman collection: Open the JSON file

Postman environment: Open the JSON file

and be sure to set the existent environment variables with your own values.

For more information about Postman collections and environments:

Game Descriptors and Properties for Providers

Definitions

The external app/data provider needs to describe its metadata on the GameBus platform. Specifically, you will have to provide so-called “game descriptors” for the application, listing which properties are available for each game session. For each property, a type needs to be specified. The currently available types are:

  • BOOL

  • COORDINATE

  • DATE

  • DOUBLE

  • INT

  • PERCENTAGE

  • STRING

  • TIME

In summary, for your external provider, you should supply the following metadata:

  • A list of game descriptors,

  • A list of properties, with their corresponding type.

Or you can just reuse available game descriptors and properties or use an extension of them.

  • Example of a GameSession:

{
  "gameDescriptorId": 101,
  "properties": [
    {
      "id": 1001,
      "value": "2015-09-29",
      "name": "Started at date",
      "type": "DATE"
    },
    {
      "id": 1002,
      "value": "01:30 AM",
      "name": "Started at time",
      "type": "TIME"
    },
    {
      "id": 1003,
      "value": 72,
      "name": "Seconds to Complete",
      "type": "INT"
    },
    {
      "id": 1004,
      "value": 321,
      "name": "Score",
      "type": "INT"
    },
    {
      "id": 1005,
      "value": 123,
      "name": "Level",
      "type": "INT"
    }
  ]
}

In this example, the “name” and “type” attributes are provided just for documentation purposes. They are fully ignored by our API since the actual property name and type information is already known inside GameBus. Put differently, only the attributes “id” and “value” matter in the array of property instance.

Game Descriptors and Properties Examples

In the following spreadsheet, you will find available Game Descriptors and properties for "GameBus" Provider as well as an example integrated provider like "Google Fit". Please pay attention to the defined combinations of Providers, Game Descriptors and Properties. This should provide you an example how different game descriptors and properties can be defined and combined for an example integrated data provider.

GameBus Developer Portal

Developer portal provides ''in-app functionalities for GameBus developers to efficiently create and test new (data provider) integrations without affecting challenges/circles unrelated to those development while keeping the admins in control of the permissions of specific developers.''

Developers Portal Panel

This panel is only visible to users with a DEV role. Developer portal page can be found under the 'Developer' tab in the settings page - An admin panel page, only visible for users with an ADMIN role - Shows up in the navigation bar when a user has an ADMIN role.

Every user can request a DEV role by clicking the 'request' button under the 'Developer' tab in the settings page.

On the developers portal users can: - Create new DataProvider instances - Create new PropertyWritePermissions for DataProviders (or delete existing ones) - Request public access for certain PropertyWritePermissions associated with created DataProviders - Create beta Circles, used for testing DataProviders on existing users - Configure the callbackURL used to communicate when a user connects to a custom DataProvider.

On the admin panel users can: - Control DEV role requests of users - Control PropertyWritePermission requests of users.

DevPanel

Request Dev Role

Go to Settings>Developer and then click 'request developer role'.

You should then wait for the admin to approve/disapprove the respective request.

Create New DataProvider

Go to DevPanel>Data Providers and then click the orange plus-sign on the top left. Fill in all necessary fields and then Click CREATE DATA PROVIDER.

DevPanel CreateNewDataProvider

The newly created provider can then be edited later if needed.

DevPanel EditCreatedDataProvider Type

Create A New Property Write Permission

Go to DevPanel>Data Providers and click on the DataProvider you want to create a property write permission for. Click 'manage property permissions and then click on the orange plus sign. Choose a gameDescriptor and property and click 'create property permission'.

DevPanel ManagePropertyPermissions

Invite to betaCircle

Go to DevPanel>Data Providers and then click on the DataProvider you want to control the betaCircle for. Click 'edit beta circle'. Click 'invite players'. Search players and check boxes and then click 'invite players'.

DevPanel UpdateCircle InvitePlayers

Accept circle invite

You should accept the circle invite like any other circle invite by going to the 'Circles' tab.

Test dataProvider by posting gameSessions on betaCircle

Connect to DataProvider

Let beta player connect to dataProvider. Go to Settings>Data. Click 'connect' for the respective DataProvider and go through the permission questions.

DevPanel ConnectToDataProvider

Get player’s oauth token via callbackUrl

GameBus will send a POST request to the callbackUrl of the respective DataProvider with the following request parameters: 'player_id': \<\<id of player connecting>> 'access_token': \<\<generated oauth token for player>> 'refresh_token': \<\<generated refresh token for player>>

Post activities

Send POST request to {{base_url}}/activities with the player’s oauth token as a bearer token. Make sure to only post on the gameDescriptors and properties you have PWPs (Property Write Permissions) for. Make sure you include the right playerId (Id of the player you want to post for, i.e. the player associated with the bearer token).

Request public access for PWPs

Go to DevPanel>Data Providers. Click on the DataProvider you want to control the betaCircle for. Click 'manage property permissions'. Check the boxes for the PWPs you want to make public. Click 'make public'.

DevPanel ManagePropertyPermissions

You should then wait for the admin to approve/disapprove the respective request.

GameBus API Client

How can I implement my own client?

Basic GET Implementation

Unirest-Java can be a good choice here, as it is a very easy to use lightweight HTTP client library. An example of a basic implementation made on top of it able to send a GET request to GameBus api is as follows:

/**
 * GET request able to receive an object as response.
 *
 * @param action to be requested
 * @param queryParams used by the request
 * @param responseClass for generic types inference
 * @param onSuccess callback for success handling
 * @param onFailure callback for error handling
 * @param <T> response object type
 * @return The http response as an object
 */
public static <T> T GET(String action, Map<String, Object> queryParams, Class<T> responseClass, Consumer<HttpResponse<T>> onSuccess, Consumer<HttpResponse<Error>> onFailure) {
	return Unirest.get(String.format("%s/%s", Config.API.getUrl(), action))
                .queryString(queryParams)
                .asObject(responseClass)
                .ifSuccess(onSuccess)
                .ifFailure(Error.class, onFailure)
                .getBody();
}

where

Config.API.getUrl()

points to the GameBus api url in use (e.g. api3.gamebus.eu), and is set by an specialized configuration object holding all needed constants/values.

It is worth noting that this implementation is here only to illustrate how easy it is to extend/make use of Unirest-Java to implement your own client. But it is worth mentioning as well that any implementation able to use the GameBus API is fine, as long as it does the trick for the developer.

Error Handling

It’s important to highlight the two callback parameters that can be used for error handling:

Consumer<HttpResponse<T>> onSuccess

is called when the requests succeeds, so the client is able to make use of the information stored in the HTTP response object passed as argument to the callback.

Consumer<HttpResponse<Error>> onFailure

is triggered on failure, and the HTTP response object holds information about the issue/error that happened causing the request/response failure.

For more information about how it works: https://kong.github.io/unirest-java/#responses

Request Header for Authorization

The authorization token that allows the requests to be performed is mandatory, as mentioned before, and must be always set as a header. By using Unirest-Java's config object this can be set as a default Header for every request:

Unirest.config().setDefaultHeader("Authorization", String.format("Bearer %s", Config.AUTH.getToken()));

where

Config.AUTH.getToken()

contains the client’s authorization token value.

Creating Pojo classes from response JSON

You can create Pojo classes from the json responses online using sites like: http://www.jsonschema2pojo.org/ There is also a possibility to add a gradle or maven plugin which can generate the classes for you as well.

Game Descriptors

In order to have an overview on all available GameDescriptors, please have a look at the json response from ActivitySchemes GET request.

Request structure

$ curl 'http://localhost:8080/activityschemes' -i \
    -H 'Accept: application/json'

Example response

User SignUp

Request structure

Unresolved directive in index.adoc - include::C:\workspace\second-workspace\gamebus-back-end-3.x\spring\build\generated-snippets/test-sign-up/curl-request.adoc[]

Example response

Unresolved directive in index.adoc - include::C:\workspace\second-workspace\gamebus-back-end-3.x\spring\build\generated-snippets/test-sign-up/http-response.adoc[]

Create and store an activity using names and translation keys (application/json content type)

Request structure

$ curl 'http://localhost:8080/me/activities' -i -X POST \
    -H 'Content-Type: application/json' \
    -H 'Authorization: Bearer 455a2d79-d83a-4445-aa8b-64fd7cf4ed50' \
    -d '{
  "gameDescriptorTK" : "WALK",
  "dataProviderName" : "Google Fit",
  "image" : "http://kowedes.nl/gb/mock/activities/run-together.jpg",
  "date" : 1614663499477,
  "propertyInstances" : [ {
    "propertyTK" : "STEPS",
    "value" : 200
  } ],
  "players" : [ 103766 ]
}'

Example response

HTTP/1.1 200 OK

Create and store an activity using IDs (application/json content type)

Request structure

$ curl 'http://localhost:8080/me/activities' -i -X POST \
    -H 'Content-Type: application/json' \
    -H 'Authorization: Bearer 000000ijjk876-d83a-4445-aa8b-000000jsjaj77' \
    -d '{
  "gameDescriptor" : 68,
  "dataProvider" : 1,
  "miniGameUse" : 1,
  "date" : 1614663499477,
  "propertyInstances" : [ {
    "property" : 112,
    "value" : "{\"type\":\"word\",\"word\":{\"x\":9,\"y\":0,\"d\":\"y\",\"word\":\"emir\",\"input\":\"****\",\"question\":\"Arabische titel\",\"complete\":true},\"timestamp\":1614663505305}"
  }, {
    "property" : 113,
    "value" : "{\"type\":\"word\",\"word\":{\"x\":9,\"y\":0,\"d\":\"y\",\"word\":\"emir\",\"input\":\"****\",\"question\":\"Arabische titel\",\"complete\":true},\"timestamp\":1614663505305}"
  }, {
    "property" : 114,
    "value" : true
  }, {
    "property" : 115,
    "value" : null
  }, {
    "property" : 116,
    "value" : false
  }, {
    "property" : 117,
    "value" : null
  }, {
    "property" : 118,
    "value" : "1"
  }, {
    "property" : 119
  } ],
  "players" : [ 495 ]
}'

Example response

HTTP/1.1 200 OK

Create and store an activity (multipart/form-data content type)

Request structure

$ curl --location --request POST 'http://localhost:8024/v2/activities?dryrun=false' \
--header 'Content-Type: multipart/form-data' \
--header 'Authorization: Bearer 4285eeeekk61-ca78-461d-9f45-hsgah7777829' \
--form 'activity="{
   \"gameDescriptor\":1,
   \"dataProvider\":5,
   \"image\": \"http://kowedes.nl/gb/mock/activities/run-together.jpg\",
   \"date\": 1616422246211,
   \"propertyInstances\":[
      {
         \"property\": 1,
         \"value\":15
      }
   ],
   \"players\":[
       459
   ]
}"'

Example response

HTTP/1.1 201 Created
Content-Type: application/json;charset=UTF-8


[
    {
        "id": 20024,
        "date": 1605795049041,
        "isManual": false,
        "group": "MTYwNTc4MDIyODk0M2VmaGZIeEFa",
        "image": "http://kowedes.nl/gb/mock/activities/run-together.jpg",
        "creator": {
            "id": 473,
            "user": {
                "id": 465,
                "firstName": "Jenkins",
                "lastName": "User",
                "image": null
            }
        },
        "player": {
            "id": 473,
            "user": {
                "id": 465,
                "firstName": "Jenkins",
                "lastName": "User",
                "image": null
            }
        },
        "gameDescriptor": {
            "id": 2,
            "translationKey": "RUN",
            "image": "https://api3.gamebus.eu/v2/uploads/public/brand/gd/icon/RUN.png",
            "type": "PHYSICAL",
            "isAggregate": false
        },
        "dataProvider": {
            "id": 5,
            "name": "Google Fit",
            "image": "https://api3.gamebus.eu/v2/uploads/public/brand/dp/GoogleFit.png",
            "isConnected": false
        },
        "propertyInstances": [
            {
                "id": 51344,
                "value": "654",
                "property": {
                    "id": 1,
                    "translationKey": "STEPS",
                    "baseUnit": "count",
                    "inputType": "INT",
                    "aggregationStrategy": "SUM",
                    "propertyPermissions": [
                        {
                            "id": 646,
                            "index": 0,
                            "lastUpdate": null,
                            "decisionNote": null,
                            "state": "PUBLIC_APPROVED",
                            "allowedValues": []
                        },
                        {
                            "id": 650,
                            "index": 0,
                            "lastUpdate": null,
                            "decisionNote": null,
                            "state": "PUBLIC_APPROVED",
                            "allowedValues": []
                        },
                        {
                            "id": 653,
                            "index": 0,
                            "lastUpdate": null,
                            "decisionNote": null,
                            "state": "PUBLIC_APPROVED",
                            "allowedValues": []
                        },
                        {
                            "id": 658,
                            "index": 0,
                            "lastUpdate": null,
                            "decisionNote": null,
                            "state": "PUBLIC_APPROVED",
                            "allowedValues": []
                        },
                        {
                            "id": 663,
                            "index": 0,
                            "lastUpdate": null,
                            "decisionNote": null,
                            "state": "PUBLIC_APPROVED",
                            "allowedValues": []
                        },
                        {
                            "id": 668,
                            "index": 0,
                            "lastUpdate": null,
                            "decisionNote": null,
                            "state": "PUBLIC_APPROVED",
                            "allowedValues": []
                        },
                        {
                            "id": 674,
                            "index": 0,
                            "lastUpdate": null,
                            "decisionNote": null,
                            "state": "PUBLIC_APPROVED",
                            "allowedValues": []
                        },
                        {
                            "id": 678,
                            "index": 0,
                            "lastUpdate": null,
                            "decisionNote": null,
                            "state": "PUBLIC_APPROVED",
                            "allowedValues": []
                        },
                        {
                            "id": 681,
                            "index": 0,
                            "lastUpdate": null,
                            "decisionNote": null,
                            "state": "PUBLIC_APPROVED",
                            "allowedValues": []
                        },
                        {
                            "id": 686,
                            "index": 0,
                            "lastUpdate": null,
                            "decisionNote": null,
                            "state": "PUBLIC_APPROVED",
                            "allowedValues": []
                        },
                        {
                            "id": 691,
                            "index": 0,
                            "lastUpdate": null,
                            "decisionNote": null,
                            "state": "PUBLIC_APPROVED",
                            "allowedValues": []
                        },
                        {
                            "id": 696,
                            "index": 0,
                            "lastUpdate": null,
                            "decisionNote": null,
                            "state": "PUBLIC_APPROVED",
                            "allowedValues": []
                        },
                        {
                            "id": 729,
                            "index": 0,
                            "lastUpdate": null,
                            "decisionNote": null,
                            "state": "PUBLIC_APPROVED",
                            "allowedValues": []
                        },
                        {
                            "id": 740,
                            "index": 0,
                            "lastUpdate": null,
                            "decisionNote": null,
                            "state": "PUBLIC_APPROVED",
                            "allowedValues": []
                        }
                    ]
                }
            }
        ],
        "personalPoints": [],
        "supports": null,
        "chats": null
    }
]

Delete an already existing activity

Request structure

$ curl 'http://localhost:8080/activities/20006' -i -X DELETE \
    -H 'Accept: application/json'

Example response

HTTP/1.1 201 Created

Read an already existing activity

Request structure

$ curl 'http://localhost:8080/activities/20006' -i \
    -H 'Accept: application/json'

Example response

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Content-Length: 3408

{
  "id" : 20006,
  "date" : 1605622222003,
  "isManual" : true,
  "group" : "MTYwNTYyNDc0MjM4NExEbmlvdHdu",
  "image" : "http://kowedes.nl/gb/mock/activities/run-together.jpg",
  "creator" : {
    "id" : 473,
    "user" : {
      "id" : 465,
      "firstName" : "Jenkins",
      "lastName" : "User",
      "image" : null
    }
  },
  "player" : {
    "id" : 473,
    "user" : {
      "id" : 465,
      "firstName" : "Jenkins",
      "lastName" : "User",
      "image" : null
    }
  },
  "gameDescriptor" : {
    "id" : 2,
    "translationKey" : "RUN",
    "image" : "https://api3.gamebus.eu/v2/uploads/public/brand/gd/icon/RUN.png",
    "type" : "PHYSICAL",
    "isAggregate" : false
  },
  "dataProvider" : {
    "id" : 1,
    "name" : "GameBus",
    "image" : "https://api3.gamebus.eu/v2/uploads/public/brand/dp/GameBus.png",
    "isConnected" : false
  },
  "propertyInstances" : [ {
    "id" : 51316,
    "value" : "500",
    "property" : {
      "id" : 1,
      "translationKey" : "STEPS",
      "baseUnit" : "count",
      "inputType" : "INT",
      "aggregationStrategy" : "SUM",
      "propertyPermissions" : [ {
        "id" : 646,
        "index" : 0,
        "lastUpdate" : null,
        "decisionNote" : null,
        "state" : "PUBLIC_APPROVED"
      }, {
        "id" : 650,
        "index" : 0,
        "lastUpdate" : null,
        "decisionNote" : null,
        "state" : "PUBLIC_APPROVED"
      }, {
        "id" : 653,
        "index" : 0,
        "lastUpdate" : null,
        "decisionNote" : null,
        "state" : "PUBLIC_APPROVED"
      }, {
        "id" : 658,
        "index" : 0,
        "lastUpdate" : null,
        "decisionNote" : null,
        "state" : "PUBLIC_APPROVED"
      }, {
        "id" : 663,
        "index" : 0,
        "lastUpdate" : null,
        "decisionNote" : null,
        "state" : "PUBLIC_APPROVED"
      }, {
        "id" : 668,
        "index" : 0,
        "lastUpdate" : null,
        "decisionNote" : null,
        "state" : "PUBLIC_APPROVED"
      }, {
        "id" : 674,
        "index" : 0,
        "lastUpdate" : null,
        "decisionNote" : null,
        "state" : "PUBLIC_APPROVED"
      }, {
        "id" : 678,
        "index" : 0,
        "lastUpdate" : null,
        "decisionNote" : null,
        "state" : "PUBLIC_APPROVED"
      }, {
        "id" : 681,
        "index" : 0,
        "lastUpdate" : null,
        "decisionNote" : null,
        "state" : "PUBLIC_APPROVED"
      }, {
        "id" : 686,
        "index" : 0,
        "lastUpdate" : null,
        "decisionNote" : null,
        "state" : "PUBLIC_APPROVED"
      }, {
        "id" : 691,
        "index" : 0,
        "lastUpdate" : null,
        "decisionNote" : null,
        "state" : "PUBLIC_APPROVED"
      }, {
        "id" : 696,
        "index" : 0,
        "lastUpdate" : null,
        "decisionNote" : null,
        "state" : "PUBLIC_APPROVED"
      }, {
        "id" : 729,
        "index" : 0,
        "lastUpdate" : null,
        "decisionNote" : null,
        "state" : "PUBLIC_APPROVED"
      }, {
        "id" : 740,
        "index" : 0,
        "lastUpdate" : null,
        "decisionNote" : null,
        "state" : "PUBLIC_APPROVED"
      } ]
    }
  } ],
  "personalPoints" : [ ],
  "supports" : null,
  "chats" : null
}

Integration of New Games

Objective

'There is a multitude of mHealth applications that aim to solve societal health problems by stimulating specific types of physical activities via gamification. However, physical health activities cover just one of the three World Health Organization (WHO) dimensions of health. The novel notion of Unified Health Gamification (UHG) is to cover besides physical health also social and cognitive health and well-being. In fact, according to the WHO, it is even too restrictive to only consider physical activities: “Health is a state of complete physical, mental and social well-being”

UHG is based on the following principles: (1) people should be capable to form teams with peers regardless of differences in health interests, (2) people should receive positive feedback at the personal level as well as on the level of their team, and (3) the implementation should be flexible with regards to the rules of health games (A. Shahrestani, 2017).'

GameBus is the first UHG application which enables the integration to third activity applications and wearables as well as games.

The goal is to enable the integration of interactive games so that game developers can log GameBus activities from their game’s events.

GameBus design allows us to define a game as an activity. This means that every successful game event can be saved as an activity. Game Rules can also be defined as "Challenge Rules" including "Property Conditions". An additional advantage is a database service GameBus provides. In the following sections, the "Crossword" game is used as an example to ensure that these definitions are explained well and the integration of other new games can be done more easily consequently.

Crossword as an Integrated Game

Crossword is used for the illustration purposes to show how an example mini game is integrated in the GameBus. In order to play the game, one can add an activity/gameSession of type "crossword" and start playing. However, a challenge should be available to play a rewarded game where user can gain points according to the defined challenge rules.

Challenge Rules

Challenge rules serve as the game rules. When creating the challenge, rules and conditions of the rules can be defined. These challenges are then listed under the "Rewarded activities" tab in the create activity page. These rules are thoroughly explained in the following section of "Crossword Walkthrough". In the following picture rules of an example challenge "Begin of 2021 Crossword" are shown.

GameRulesInChallengeRules

Crossword Walkthrough

An unrewarded game activity can be created by choosing "crossword" from all activities menu as it is shown in the picture.

UnrewardedGameActivity

Game activity can also be created within the challenge and in this case it will be a rewarded activity which means it changes the balance points of the player with respect to the defined rules.

CreateRewardedActivity

As mentioned earlier, game rules can be defined as challenge rules. In this example, 3 rules are defined:

  • Take a Hint: If the player takes a hint for a word, then 1 point will be reduced.

  • Enter Correct Word: Upon guessing the correct word, player gains 5 points.

  • Finish Puzzle: Upon finishing the puzzle, the player gains 20 points.

While creating a challenge, these game rules can be defined. Creating a challenge with these example game rules is illustrated in the following pictures.

A rule can be defined by giving it a name and a correct activity type. Furthermore, "Conditions" section can be used to define the rule properties and "Point maps" section is used to define a mapping point for the rule.

  • Defining the rule "Enter Correct Word":

CreateChallenge Rule EnterCorrectWord

  • Defining the rule "Take a Hint":

CreateChallenge Rule TakeAHint

  • Defining the rule "Finish Puzzle":

CreateChallenge Rule FinishPuzzle

After the challenge is created and the player is joined to the challenge. A new activity can be created within the challenge in the "Create activity" page and in the "Rewarded Activities" tab.

CreateRewardedActivity

It is explained in the following how these rules look like when playing the game.

Rule 1: Take a Hint

In order to take a hint, anywhere on the square where the word is not guessed should be clicked and then press the hint button.

ClickForHint

A hint letter for that word will be shown and the points will be reduced by 1 point.

HintIsShownInGreen 1PointDeducted

Rule 2: Enter Correct Word

For guessing the correct word, cursor should be moved to the question that the player wants to answer. You can also use the back/next arrows to change between question.

CreateAnActivity EnterTheCorrectWord

If the correct word is entered, then the player gains 5 points and the correct word is shown in green letters.

GainingPointsForCompleteWord WordInGreen

Rule 3: Finish Puzzle

Upon finishing the puzzle, the player gains 20 extra points.

Checklist for the integration of a new game

The goal in this section is to show a flow of required actions that need to be done and checked for the integration of a new mini game. Having these points, one can get a broader perspective of all required steps. Further details are provided in the following sections.

  • A GameDescriptor for the mini game is created. In most cases, this is equivalent to the name of the mini game.

  • All required properties for the mini game are created taking into consideration reusing of already existing properties like: "MINIGAME_DATA", "RESET", …​ (Please contact gameBus team or use the developer portal)

  • All necessary property permissions are defined or granted. (Please contact gameBus team or use the developer portal)

  • As part of the mini game implementation, listen to message passing from parent. This message includes information about the gameDescriptor, propertyInstances, challenge, rules, conditions and so on. This information may be used then for implementation of the mini game. (please see the section "Communicated "message" between miniGame iFrame and GameBus Frontend")

  • In the mini game implementation, message should be sent to the parent upon events happening in the mini game. For that an activityForm should be defined. Then the list of activityForms should be sent to the parent. (please see the section "ActivityForm Build in the Mini Game IFrame")

  • A fixed structure is defined for GameDump. The GameDump includes the data for the game. It should be designed so that the main structure remains fixed and the values change throughout playing the game. A new propertyInstance for the property MINIGAME_DATA can be created and be patched into the ActivityForm.

  • Tip for implementation: the propertyInstance for the property "MINIGAME_DATA" holds the state of the game data and can be used to retrieve the state for the mini game.

  • Tip for implementation: upon events in the mini game, propertyInstances should be created and ActivityForm should be patched. Loop over all the propertyPermissions of "scheme" sent from parent in the message and set the values. Then patch these propertyInstances to the activityForm. (For an example implementation, please refer to "miniGameCrossword" project)

  • If RESET is a valid functionality for the mini game. The already existing "RESET" property can be used to create a propertyInstance of ActivityForm.

  • New entries in the mini game tables are added. (For more info, please contact the gameBus team) In this step, some information about the game should be provided by the developer of mini game:

    • A "URL" under which the mini game can be accessed

    • An initial "gameDump" string representing the initial state of the mini game.

    • Information whether the game state can be rendered in a result page for each posted gameSession. Alternative is that the result page just includes information about the propertyInstances of the gameSession.

    • Whether different gameDumps with regard to different complexities are available.

Cross Domain Communication

For the integration of the games, it is considered that a cross domain communication should take place. This means that the game may be hosted on a different domain comparing to GameBus. For this purpose and to be able to illustrate our crossword example, a new host is considered under the domain: minigames.gamebus.eu

The frontend integration considers using an iFrame to call the mini game (e.g: crossword game). An iFrame is an inline frame used inside a webpage to load another HTML document inside it. The main challenge here is to keep the communication between iFrame (mini game) and the parent (gamebus frontend) as simple as possible with regards to the performance. Api request calls to retrive or persist data will be handled in the parent (gamebus frontend) whereas presenting the data will be handled in the iFrame (mini game). This data includes for example the correct words that are entered so far and the updated balance points.

embeddedIFrame

Minigame creates the game based on input data from the parent so called GameDumps including the game data, balance points and so on. Game creates events based on the variables in the gameDump which explain events happening in the game (e.x: word is complete or a hint is taken). In the following section, gameDump structure is explained in more detail.

Implementation

First Steps

Definitions of GameDescriptor, GameDump and Properties

Before starting with the integration, there are few preparations which are needed to be considered:

  • Defining a new game descriptor

  • Defining required properties

  • Defining GameDump(s)

In the following, these requirements are explained in detail with respect to our crossword mini game example.

In order to integrate a new game, a few GameBus definitions should be declared which are used to define a game and persist game events. The crossword example is also here taken to explain the application.

The following json shows the "Crossword" "GameDescriptor" which is defined for the "Gamebus" "DataProvider". And it contains required "Properties". These properties are also explained later in this section.

A game activity can be created by choosing the game descriptor of "Crossword". It means that for the integration of a new game, a new game descriptor for that game should be added.

(for more information how to integrate a new DataProvider with GameDescriptors and Properties, please refer to: https://devdocs.gamebus.eu/#GameBus%20Developer%20Portal)

Furthermore, the game data are defined in the form of a gameDump. The gameDump is a string and kept in the database. The initial data dump is therefore available in the miniGameState table. This initial data dump can then be retrieved again if the game is supposed to implement a reset functionality.

miniGameState table has an "id", an integer "complexity" value for the game and a string "gameDump". If the gameDump can be defined differently with respect to complexity of the game, then it is recommended to define more than one gameDump with different complexities. Please notice that in this case the main structure of the gameDump remains same.

In order to understand how an example gameDump can be defined, please have a look at the gameDump for the CROSSWORD.

A gameDump for the "Crossword" game contains information like list of all the words in the puzzle. Open the json file in the following to see an example gameDump. Each word in the puzzle has following attributes:

  • x: horizonal position

  • y: vertical position

  • d: vertical or horizontal direction with values of x or y

  • word: the solution word

  • input: input from the player

  • question: the crossword question

  • complete: boolean declaring whether the player has played the word or not

Here is an example gameDump for CROSSWORD mini game:

Finally, correct properties should be available. Posting a crossword activity creates a GameSession and the related PropertyInstances for the following properties:

  • MINIGAME_DATA: It stores the gameDump as value

  • MINIGAMESTATE_ID: It has the MiniGameStateID as value

  • COMPLEXITY: It stores the miniGameState complexity as value.

  • WORD_ENTERED: It stores the new word which is entered.

  • THROUGH_HINT: It stores true as value if the hint is used.

  • WORD_CORRECT: It stores the new word which is correct.

  • RESET: It stores true as value if the reset button is clicked.

In order to integrate a new game other than the crossword game, appropriate properties for that game need to be defined and implemented.

You should make sure that upon events in your game, you may need to instantiate the correct relevant property. Create and path propertyInstances. Create an activityForm and path all propertyInstances in it and send it to the parent. In the next section, it is described how you can create an activityForm in more detail.

Therefore as a game developer, you just need to send a list of activityForms to GameBus.

Further api calls to persist the activities will be handled on the GameBus side. This means that GameBus adds each success or event in the game as a new activity instance (gameSession). Additionally, all related propertyInstances for the defined properties will be created.

ActivityForm Build in the Mini Game IFrame

once gameDescriptor, dataProvider and properties are defined. An activity can be created. Details about the structur can be found in the following.

this.activityForm = this.fb.group({
      gameDescriptor: [this.sid, Validators.compose([Validators.required])],
      dataProvider: [1, Validators.compose([Validators.required])],
      miniGameUse: this.miniGameUse,
      date: [this.datetime.now],
      propertyInstances: this.fb.array([]),
      property: null,
      value: null,
      players: [[this.pid], Validators.compose([Validators.required])],
    });

Here is an example built activityForm:

ActivityForm01

After each successful event the activityForm or list of activityForms should be sent to the parent frontend so that these activities will be posted and persisted.

For more information how you can send this message from the mini game to the parent frontend, please see the section "Communicated "message" between miniGame iFrame and GameBus Frontend". For more information about the integration of mini game in the parent frontend and used frameworks, please refer to the section "Frontend Integration".

Frontend Integration

The miniGame is included as an embedded iframe in the parent and exists in a different domain. It can be possible to implement the minigame with any framework keeping in mind that posting messages to the parent window is possible. Again for illustration purposes, we can have a look at how Crossword miniGame is implemented.

Please clone the repository for a closer look: (https://bitbucket.org/vitalithree/minigame-crossword/src/master/)

The Javascript code for Crossword is written using CANVAS Api (for more information, please see: https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API)

It is then included into the html file using the canvas html tag and further necessary buttons for the game:

<div class="gameArea">
    <canvas class="crossword"></canvas>
    <ion-button (click)="resetGame()">Reset Game</ion-button>
    ...
</div>

The canvas JavaScript implementation is then implemented in the typeScript file.

Finally, the game will be initiated by calling the crossword function and passing the gameDump property sent from the parent to the iFrame.

let game = new Crossword(gameDump, document.querySelector(".gameArea"), true);

Upon pressing the reset button, message will be sent from iFrame to the parent frontend. This message is explained in more detail later.

The call sequence is declared for the event example of guessing the correct word by the player in the following diagram.

Call Sequence

miniGame SequenceDiagram

As it is also shown in the diagram, iFrame and parent GameBus frontend communicate via a json message which is declared in more detail in the following section.

In the miniGame, we should therefore implement posting the message to the parent window:

window.parent.postMessage(message, '*')

Communicated "message" between miniGame iFrame and GameBus Frontend

Depending on the mini game logic, a message should be transferred from the parent GameBus frontend to the embedded miniGame iFrame and reversely from the iFrame to the parent. In the crossword for example, message send from parent to iFrame contains the following parameters:

    var message = JSON.stringify({
        scheme:this.scheme,
        activity:this.activity,
        gameStateId:this.gameStateId,
        summary:this.summary,
        pid:this.pid,
        game:this.game,
        gameDump:this.gameDmp,
        reset: this.reset,
        newDump:this.newDump,
        leastPoint: this.leastPoint,
        miniGameUse: this.miniGameUse,
        challengeId: this.cid,
        activeChallengeId: this.activeChallengeId
    });
  • scheme: contains gameDescriptor and propertyPermissions and defined in the play page of the activity

  • activity: gameDescriptor and propertyPermissions and is defined only in the result page of the activity

  • gameStateId: game state id referring to the MiniGameState table to retrieve the gameDump

  • summary: summary contains the challenge info

  • pid: player id

  • game: the initiated game from gameDump

  • gameDump: gameDump including game data

  • reset: reset flag indicating if the message was sent after a reset

  • newDump: flag to indicate if the dump has been changed

  • leastPoint: balancePoints of the player calculated newly

  • challengeId: the challengeId if a challenge is defined for the game

  • activeChallengeId: the challengeId if an active challenge exists for the game

And the message from iFrame back to the parent contains the following parameters:

const message = JSON.stringify({
        activityForms:this.activityForms
    });
  • activityForms: list of activityForms which should be persisted at the parent side

In order to post the message, the regarding post in the mini game should be implemented which posts a message to the parent for further processing. This processing may include api request calls to the GameBus backend which are included in the parent project.

window.parent.postMessage(message, '*');

Crossword was just chosen as an example to be able to explain the case better. However, one should think in terms of the game which should be integrated and find out which parameters should be included in the communicated message. However, for example the good practice is that the game always listens to the gameDump coming from the parent which is retrieved from database. Following by persisting each activity, the balance points should be calculated newly and be sent to the mini game again. The reason is that other activities other than game like walking, running and so on can also change the balance points. Furthermore, mini game should include the activityForms which should be posted in the message to the parent. So that each activity is then persisted via api requests which are in the parent project.

References

  • Shahrestani, A., Van Gorp, P., Le Blanc, P., Greidanus, F., de Groot, K., & Leermakers, J. (2017, July). Unified health gamification can significantly improve well-being in corporate environments. In 2017 39th Annual International Conference of the IEEE Engineering in Medicine and Biology Society (EMBC) (pp. 4507-4511). IEEE.